elixir-deployment

Elixir deployment patterns: Mix releases, Docker multi-stage builds, clustering with libcluster, runtime config, health checks, observability. Use when deploying Elixir/Phoenix to production.

Best use case

elixir-deployment is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Elixir deployment patterns: Mix releases, Docker multi-stage builds, clustering with libcluster, runtime config, health checks, observability. Use when deploying Elixir/Phoenix to production.

Teams using elixir-deployment should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/elixir-deployment/SKILL.md --create-dirs "https://raw.githubusercontent.com/ratnesh-maurya/cursor-claude-personas/main/senior-elixir-developer/.claude/skills/elixir-deployment/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/elixir-deployment/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How elixir-deployment Compares

Feature / Agentelixir-deploymentStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Elixir deployment patterns: Mix releases, Docker multi-stage builds, clustering with libcluster, runtime config, health checks, observability. Use when deploying Elixir/Phoenix to production.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Elixir Deployment Patterns

Production deployment strategies for Elixir and Phoenix applications.

## When to Use

- Building release artifacts for production deployment
- Containerizing Elixir apps with Docker
- Setting up BEAM clustering across nodes
- Configuring observability and health checks

## Mix Releases

```elixir
# mix.exs
def project do
  [
    app: :my_app,
    releases: [
      my_app: [
        include_executables_for: [:unix],
        steps: [:assemble, :tar]
      ]
    ]
  ]
end
```

Build:
```bash
MIX_ENV=prod mix release
```

Run migrations in production:
```bash
bin/my_app eval "MyApp.Release.migrate()"
```

### Release Migration Module

```elixir
defmodule MyApp.Release do
  @app :my_app

  def migrate do
    load_app()
    for repo <- repos() do
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

  defp repos, do: Application.fetch_env!(@app, :ecto_repos)
  defp load_app, do: Application.load(@app)
end
```

## Runtime Config

Always use `config/runtime.exs` for production values (reads env vars at boot, not compile time):

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

if config_env() == :prod do
  config :my_app, MyApp.Repo,
    url: System.fetch_env!("DATABASE_URL"),
    pool_size: String.to_integer(System.get_env("POOL_SIZE", "10"))

  config :my_app, MyAppWeb.Endpoint,
    url: [host: System.fetch_env!("PHX_HOST"), port: 443, scheme: "https"],
    http: [port: String.to_integer(System.get_env("PORT", "4000"))],
    secret_key_base: System.fetch_env!("SECRET_KEY_BASE")
end
```

## Docker Multi-Stage Build

```dockerfile
# Stage 1: Build
FROM hexpm/elixir:1.17.3-erlang-27.1-debian-bookworm-20240904-slim AS build

RUN apt-get update && apt-get install -y build-essential git && rm -rf /var/lib/apt/lists/*

WORKDIR /app
ENV MIX_ENV=prod

COPY mix.exs mix.lock ./
RUN mix deps.get --only prod && mix deps.compile

COPY config config
COPY lib lib
COPY priv priv
COPY assets assets

RUN mix assets.deploy && mix compile && mix release

# Stage 2: Runtime
FROM debian:bookworm-slim AS runtime

RUN apt-get update && apt-get install -y libssl3 libncurses6 locales && rm -rf /var/lib/apt/lists/*
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG=en_US.UTF-8

WORKDIR /app
COPY --from=build /app/_build/prod/rel/my_app ./

ENV PHX_SERVER=true
EXPOSE 4000
HEALTHCHECK CMD wget -q --spider http://localhost:4000/healthz || exit 1

CMD ["bin/my_app", "start"]
```

## Health Check Endpoint

```elixir
# In router
get "/healthz", HealthController, :check

# Controller
defmodule MyAppWeb.HealthController do
  use MyAppWeb, :controller

  def check(conn, _params) do
    case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1") do
      {:ok, _} -> json(conn, %{status: "ok"})
      {:error, _} -> conn |> put_status(503) |> json(%{status: "unhealthy"})
    end
  end
end
```

## Clustering

### libcluster Setup

```elixir
# In Application supervision tree
children = [
  {Cluster.Supervisor, [topologies(), [name: MyApp.ClusterSupervisor]]},
  # ... other children
]

defp topologies do
  [
    my_app: [
      strategy: Cluster.Strategy.Kubernetes.DNS,
      config: [
        service: "my-app-headless",
        application_name: "my_app"
      ]
    ]
  ]
end
```

### Strategies

| Strategy | Use For |
|----------|---------|
| `Cluster.Strategy.Gossip` | Local dev, LAN multicast |
| `Cluster.Strategy.Kubernetes.DNS` | K8s headless services |
| `Cluster.Strategy.Epmd` | Static host list |
| `dns_cluster` (Phoenix 1.8) | Simple DNS-based discovery |

### Environment Variables

```bash
RELEASE_DISTRIBUTION=name
RELEASE_NODE=my_app@hostname
RELEASE_COOKIE=super_secret_cookie  # Always set explicitly
```

## Observability

### Telemetry + Prometheus

```elixir
# In Application
children = [
  MyApp.Telemetry,  # Your telemetry supervisor
  {TelemetryMetricsPrometheus, metrics: MyApp.Telemetry.metrics()}
]

defmodule MyApp.Telemetry do
  def metrics do
    [
      counter("phoenix.router.dispatch.stop.duration"),
      summary("phoenix.endpoint.stop.duration", unit: {:native, :millisecond}),
      counter("my_app.repo.query.total_time", unit: {:native, :millisecond}),
    ]
  end
end
```

### Structured Logging

```elixir
# config/prod.exs
config :logger, :console,
  format: {Jason, :encode!},
  metadata: [:request_id, :user_id]
```

## Hosting Options

| Platform | Clustering | Notes |
|----------|-----------|-------|
| **Fly.io** | Built-in | First-class Elixir support |
| **Gigalixir** | Built-in | Hot upgrades, native BEAM |
| **AWS ECS/Fargate** | libcluster DNS | Standard container deploy |
| **Render** | Manual | Simple for single-node |

## Production Checklist

- [ ] `config/runtime.exs` for all env vars (not compile-time config)
- [ ] `RELEASE_COOKIE` set explicitly
- [ ] Health endpoint exposed at `/healthz`
- [ ] Database migrations in deployment pipeline
- [ ] Graceful shutdown (Phoenix drains connections by default)
- [ ] Structured logging configured
- [ ] Telemetry metrics exported
- [ ] `Sobelow` security scan in CI
- [ ] `MixAudit` dependency vulnerability check in CI
- [ ] `Credo` code quality check in CI

## Anti-Patterns

- **Compile-time config for runtime values**: Use `config/runtime.exs`, not `config/config.exs`
- **Default RELEASE_COOKIE**: Always set explicitly — default is insecure
- **Skipping health checks**: Load balancers need them
- **No migration strategy**: Always automate migrations in deploy pipeline

Related Skills

elixir-testing

5
from ratnesh-maurya/cursor-claude-personas

Elixir testing patterns: ExUnit, Mox behaviour-based mocking, StreamData property testing, Phoenix integration tests. Use when writing tests for Elixir/Phoenix applications.

elixir-pro

5
from ratnesh-maurya/cursor-claude-personas

Write idiomatic Elixir code with OTP patterns, supervision trees, and Phoenix LiveView. Masters concurrency, fault tolerance, and distributed systems.

elixir-otp-patterns

5
from ratnesh-maurya/cursor-claude-personas

OTP architecture patterns for Elixir: GenServer, Supervisor, DynamicSupervisor, Registry, Application. Use when designing fault-tolerant process architectures, supervision trees, or stateful services.

elixir-concurrency

5
from ratnesh-maurya/cursor-claude-personas

Elixir concurrency patterns: Tasks, GenStage pipelines, Broadway data ingestion, Flow parallel processing, Oban background jobs. Use when building concurrent or parallel data processing systems.

odoo-docker-deployment

5
from ratnesh-maurya/cursor-claude-personas

Production-ready Docker and docker-compose setup for Odoo with PostgreSQL, persistent volumes, environment-based configuration, and Nginx reverse proxy.

makepad-deployment

5
from ratnesh-maurya/cursor-claude-personas

CRITICAL: Use for Makepad packaging and deployment. Triggers on: deploy, package, APK, IPA, 打包, 部署, cargo-packager, cargo-makepad, WASM, Android, iOS, distribution, installer, .deb, .dmg, .nsis, GitHub Actions, CI, action, marketplace

expo-deployment

5
from ratnesh-maurya/cursor-claude-personas

Deploy Expo apps to production

vercel-deployment

5
from ratnesh-maurya/cursor-claude-personas

Expert knowledge for deploying to Vercel with Next.js Use when: vercel, deploy, deployment, hosting, production.

kubernetes-deployment

5
from ratnesh-maurya/cursor-claude-personas

Kubernetes deployment workflow for container orchestration, Helm charts, service mesh, and production-ready K8s configurations.

deployment-pipeline-design

5
from ratnesh-maurya/cursor-claude-personas

Design multi-stage CI/CD pipelines with approval gates, security checks, and deployment orchestration. Use when architecting deployment workflows, setting up continuous delivery, or implementing Gi...

deployment-engineer

5
from ratnesh-maurya/cursor-claude-personas

Expert deployment engineer specializing in modern CI/CD pipelines, GitOps workflows, and advanced deployment automation.

azd-deployment

5
from ratnesh-maurya/cursor-claude-personas

Deploy containerized applications to Azure Container Apps using Azure Developer CLI (azd). Use when setting up azd projects, writing azure.yaml configuration, creating Bicep infrastructure for Cont...