# `BeamDeploy`
[🔗](https://github.com/Valian/beam_deploy/blob/v0.1.0/lib/beam_deploy.ex#L1)

Blue-green release swaps and in-process hot upgrades for a single Elixir node.

`BeamDeploy` keeps a long-lived parent node running locally and serves traffic
from a child peer node started with OTP's `:peer` module. When you hand the
parent a new `mix release` tarball, it boots a new peer with the new release,
lets both peers overlap on the same socket via `SO_REUSEPORT`, then gracefully
shuts down the old one.

The package is intentionally small:

- no storage, polling, or orchestration layer
- no Docker or platform coupling
- local tarball input only

You copy a release tarball onto the host and call either:

- `BeamDeploy.upgrade/1` for a blue-green peer swap
- `BeamDeploy.hot_upgrade/2` for an in-process hot code reload

## Integration

    defmodule MyApp.Application do
      use Application

      def start(type, args) do
        BeamDeploy.start_link(
          otp_app: :my_app,
          start: {__MODULE__, :start_app, [type, args]},
          endpoint: MyAppWeb.Endpoint
        )
      end

      def start_app(_type, _args) do
        children = [MyApp.Repo, MyAppWeb.Endpoint]
        Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
      end
    end

Enable parent/peer mode in production with either:

    config :beam_deploy, enabled: true

or:

    BEAM_DEPLOY=true

In environments where BeamDeploy is not enabled, `start_link/1` and
`start_link/2` just call your `start_app` MFA directly.

## Upgrades

    BeamDeploy.upgrade("/tmp/my_app-0.2.0.tar.gz")
    # => :ok

    BeamDeploy.hot_upgrade("/tmp/my_app-0.2.0.tar.gz", otp_app: :my_app)
    # => :ok

The tarball must be a standard `mix release` archive built with the same OTP
version as the running node.

Hot upgrades are only safe for compatible code changes. Use a cold deploy or
the blue-green path instead when changing supervision tree shape, upgrading
Erlang/OTP, changing major runtime topology, or touching NIFs.

# `enabled?`

```elixir
@spec enabled?() :: boolean()
```

Returns `true` when BeamDeploy parent/peer mode is enabled for this runtime.

# `get_all_handoff`

```elixir
@spec get_all_handoff() :: map()
```

Returns all handoff data as a map.

# `get_handoff`

```elixir
@spec get_handoff(term()) :: term() | nil
```

Reads handoff data from the parent node.

# `hot_upgrade`

```elixir
@spec hot_upgrade(
  Path.t(),
  keyword()
) :: :ok | {:error, term()}
```

Performs an in-process hot upgrade from a local release tarball path.

This path does not depend on the parent/peer runtime. It reloads code inside
the current node, suspends affected processes, runs `code_change/3`, and
resumes them.

Supported changes are limited to compatible hot-code updates built with the
same Erlang/OTP version. NIF upgrades are skipped.

# `incoming_peer`

```elixir
@spec incoming_peer() :: node() | nil
```

Returns the incoming replacement peer for the current peer, if one exists.

# `outgoing_peer`

```elixir
@spec outgoing_peer() :: node() | nil
```

Returns the outgoing peer being replaced by the current peer, if one exists.

# `peer_node`

```elixir
@spec peer_node() :: node() | nil
```

Returns the active peer node, or `nil` when BeamDeploy is not running.

# `put_handoff`

```elixir
@spec put_handoff(term(), term()) :: :ok
```

Stores handoff data that survives peer transitions.

# `start_link`

```elixir
@spec start_link(keyword()) :: Supervisor.on_start()
```

Starts BeamDeploy without parent-level children.

# `start_link`

```elixir
@spec start_link(
  [Supervisor.child_spec()],
  keyword()
) :: Supervisor.on_start()
```

Starts BeamDeploy with optional parent-level children.

Parent children run on the long-lived parent node before the peer manager.
This is useful for services that should not restart on every cutover.

# `status`

```elixir
@spec status() :: %{active_node: node() | nil, upgrading: boolean()}
```

Returns the current blue-green status map.

# `upgrade`

```elixir
@spec upgrade(Path.t()) :: :ok | {:error, term()}
```

Performs a blue-green upgrade from a local release tarball path.

The call can be made from either the parent or active peer node.

# `upgrading?`

```elixir
@spec upgrading?() :: boolean()
```

Returns `true` if a release swap is currently running.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
