{"id":23226806,"url":"https://github.com/botsquad/graceful_stop","last_synced_at":"2025-09-26T15:55:58.226Z","repository":{"id":96230686,"uuid":"140114369","full_name":"botsquad/graceful_stop","owner":"botsquad","description":"Elixir OTP application to gracefully stop the system after running shutdown hooks. Also catches SIGTERM.","archived":false,"fork":false,"pushed_at":"2024-04-09T18:18:05.000Z","size":24,"stargazers_count":21,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-24T08:27:28.086Z","etag":null,"topics":["elixir-lang","otp"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/botsquad.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}},"created_at":"2018-07-07T20:16:17.000Z","updated_at":"2024-04-24T08:27:28.087Z","dependencies_parsed_at":null,"dependency_job_id":"3f481c46-e014-414d-b02a-84be6653fce7","html_url":"https://github.com/botsquad/graceful_stop","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/botsquad%2Fgraceful_stop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/botsquad%2Fgraceful_stop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/botsquad%2Fgraceful_stop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/botsquad%2Fgraceful_stop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/botsquad","download_url":"https://codeload.github.com/botsquad/graceful_stop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230356044,"owners_count":18213573,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["elixir-lang","otp"],"created_at":"2024-12-19T00:19:31.737Z","updated_at":"2025-09-26T15:55:53.168Z","avatar_url":"https://github.com/botsquad.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GracefulStop\n\n[![Build Status](https://github.com/botsquad/graceful_stop/workflows/test/badge.svg)](https://github.com/botsquad/graceful_stop)\n[![Module Version](https://img.shields.io/hexpm/v/graceful_stop.svg)](https://hex.pm/packages/graceful_stop)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/graceful_stop/)\n[![Total Download](https://img.shields.io/hexpm/dt/graceful_stop.svg)](https://hex.pm/packages/graceful_stop)\n[![License](https://img.shields.io/hexpm/l/graceful_stop.svg)](https://github.com/botsquad/graceful_stop/blob/main/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/botsquad/graceful_stop.svg)](https://github.com/botsquad/graceful_stop/commits/main)\n\nGracefully calls `:init.stop()` after running user-configured shutdown\nhooks.\n\nAlso catches `SIGTERM` signal to gracefully stop the system and run\nthe shutdown hooks.\n\nWhen running in a Kubernetes-managed cluster, nodes in the cluster\ncome and go as kubernetes decides. It sends the SIGTERM signal, which\nby default triggers a `:init.stop()`. However, you might want to give\nthe system some time to shut down, running cleanup processes, wait for\nrunning requests to finish, et cetera.\n\n## Usage\n\nAfter adding `:graceful_stop` to your deps, you can configure it to\ncall hooks when the application will stop:\n\n```\nconfig :graceful_stop, :hooks, [\n  [IO, :puts, [\"Stopping the system\"]]\n]\n```\n\nThen: `kill $(pidof beam.smp)` sends a `SIGTERM` signal to your\nrunning BEAM process, and you will notice that you see \"Stopping the\nsystem\" printed on the console, before it shuts down.\n\nNote that these hooks run _before_ any of your OTP applications are\nbeing stopped, so you can do all kinds of things there, without\nworrying that parts of your system are already shut down (which would\nbe the case if you try to trap the `{:EXIT, pid, :shutdown}` message).\n\nThere is a `:hook_timeout` setting, defaulting to 15 seconds, which is\nthe maximum time that a hook can run. Hooks run in parallel, using\n`Task.async` / `Task.yield_many`.\n\n## Inspiration\n\nThis project was inspired by the\n[k8s_traffic_plug](https://github.com/Financial-Times/k8s_traffic_plug)\npackage and the corresponding [blog\npost](https://medium.com/@ellispritchard/graceful-shutdown-on-kubernetes-with-signals-erlang-otp-20-a22325e8ae98).\nHowever, it does not include a Plug. Creating a plug is simple, as you\ncan call `GracefulStop.get_status()` which returns either `:running`\nor `:stopping`, and you can create a plug that serves a HTTP 503\nrequest based on this code.\n\n### Phoenix Plug implementation example\n\nMount this plug inside your [Phoenix Endpoint](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html) before the router plug is mounted.\n**DO NOT** mount this plug inside your router file.\n\n```elixir\ndefmodule MyAppWeb.Endpoint do\n  use Phoenix.Endpoint, otp_app: :myapp\n\n  ....\n\n  plug MyAppWeb.Plug.TrafficDrain\n  plug MyAppWeb.Router\n```\n\nReference implementation of `TrafficDrain` plug.\n\n```elixir\ndefmodule MyAppWeb.Plug.TrafficDrain do\n  @moduledoc \"\"\"\n  Plug for handling Kubernetes readinessProbe.\n\n  Plug starts responding with 503 - Service Unavailable from `/__traffic`, when traffic is being drained.\n  Otherwise we respond with 200 - OK.\n  \"\"\"\n\n  import Plug.Conn\n\n  @behaviour Plug\n\n  @impl true\n  def init(opts), do: opts\n\n  @impl true\n  def call(%Plug.Conn{path_info: [\"__traffic\"]} = conn, _opts) do\n    case GracefulStop.get_status() do\n      :stopping -\u003e\n        conn\n        |\u003e put_resp_content_type(\"text/plain\")\n        |\u003e send_resp(:service_unavailable, \"Draining\")\n        |\u003e halt()\n\n      :running -\u003e\n        conn\n        |\u003e put_resp_content_type(\"text/plain\")\n        |\u003e send_resp(:ok, \"Serving\")\n        |\u003e halt()\n    end\n  end\n\n  @impl true\n  def call(conn, _opts) do\n    conn\n  end\nend\n\n```\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed\nby adding `graceful_stop` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:graceful_stop, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbotsquad%2Fgraceful_stop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbotsquad%2Fgraceful_stop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbotsquad%2Fgraceful_stop/lists"}