# Releases, Configuration and Deployment — Elixir & Phoenix

Source: https://www.geekswithgeeks.com/en/elixir-phoenix/s-deploy

> Build releases, configure them at run time and deploy to containers and clusters.

## Shipping Elixir applications

Elixir applications are deployed as **releases**: `MIX_ENV=prod mix release` produces a self-contained directory with your compiled code, dependencies and the Erlang runtime, so the server needs no Elixir installation. Configuration has stages: `config/config.exs` and environment files are evaluated at **build time**, while **`config/runtime.exs`** is evaluated when the release **starts**, which is where secrets and environment variables such as `DATABASE_URL`, `SECRET_KEY_BASE` and `PHX_HOST` belong. Phoenix provides `mix phx.gen.release --docker` to generate a release setup and a multi-stage **Dockerfile**. Run migrations with a release command such as `bin/migrate`, which calls a migration module, as part of deployment. Releases support `bin/shop start`, `remote` (attach an IEx shell to the running system for debugging) and `eval`. **Clustering** connects BEAM nodes so PubSub, Presence and distributed processes work across machines, often using **libcluster** with DNS or Kubernetes strategies. Platforms such as Fly.io, Gigalixir and Render have Elixir guides, and Kubernetes works well too. Observe production with **Telemetry**, Phoenix LiveDashboard and OpenTelemetry.

## From code to running release

mix release bundles code, dependencies and the runtime; runtime.exs reads configuration at boot.

![A source folder flowing into a sealed package box, then into a container shape on a server rack, with a separate small settings card feeding into the container at start.](assets/figures/elixir-phoenix/section-8-map.svg) — Figure 8.1 — Building and running a release.

## runtime.exs and a release workflow

Secrets come from the environment at boot, not at build time.

```elixir
# config/runtime.exs
import Config

if config_env() == :prod do
  database_url =
    System.get_env("DATABASE_URL") ||
      raise "environment variable DATABASE_URL is missing"

  config :shop, Shop.Repo,
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise "environment variable SECRET_KEY_BASE is missing"

  config :shop, ShopWeb.Endpoint,
    url: [host: System.get_env("PHX_HOST", "example.com"), port: 443, scheme: "https"],
    http: [ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000")],
    secret_key_base: secret_key_base
end

# Shell commands (comments):
#   mix phx.gen.release --docker        # generates Dockerfile, bin/migrate and bin/server
#   MIX_ENV=prod mix assets.deploy
#   MIX_ENV=prod mix release
#   _build/prod/rel/shop/bin/migrate    # run Ecto migrations
#   _build/prod/rel/shop/bin/server     # start the app
#   _build/prod/rel/shop/bin/shop remote   # attach IEx to the running node
```

## Build time versus boot time

Values read in `config/prod.exs` are baked into the release when it is built. Anything that differs per environment or is secret must be read in `config/runtime.exs`, or the same image cannot move from staging to production.

**Quiz:** Where should secrets such as DATABASE_URL be read in a release?

- [ ] config/config.exs
- [ ] mix.exs
- [x] config/runtime.exs, evaluated when the release starts
- [ ] Hard-coded in a module

*Answer:* config/runtime.exs, evaluated when the release starts. runtime.exs runs at boot, so each environment can supply its own values.
