oliver-kriska/claude-elixir-phoenix-pi-deploy
Elixir/Phoenix deployment patterns — Dockerfile, fly.toml, runtime.exs, mix release, rel/ overlays. Use when configuring Fly.io, Docker, CI/CD, health checks, or production migrations.
This is a copy. The original lives at oliver-kriska/claude-elixir-phoenix-deploy.
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix --skill deploy
Quick reference for deploying Elixir/Phoenix applications.
config.exs get baked into the release binary. Use runtime.exs with env vars so secrets are resolved at bootverify: :verify_peer allows MITM attacks between your app and database; production data traverses the connectionruntime.exs runs whenever arelease boots, including eval-based migration commands. Only require S3,
Redis, and similar credentials when that integration is enabled
if config_env() == :prod do
database_url = System.get_env("DATABASE_URL") || raise "DATABASE_URL is required"
secret_key_base = System.get_env("SECRET_KEY_BASE") || raise "SECRET_KEY_BASE is required"
host = System.get_env("PHX_HOST") || raise "PHX_HOST is required"
config :my_app, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
ssl: true,
ssl_opts: [verify: :verify_peer]
config :my_app, MyAppWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
http: [ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000")],
secret_key_base: secret_key_base,
server: true
end
Keep core boot secrets such as DATABASE_URL and SECRET_KEY_BASE required.
Gate credentials for optional integrations behind the same feature switch that
enables the integration:
s3_config =
if System.get_env("STORAGE_BACKEND") == "s3" do
[
access_key_id:
System.get_env("S3_ACCESS_KEY") ||
raise("S3_ACCESS_KEY is required when STORAGE_BACKEND=s3"),
secret_access_key:
System.get_env("S3_SECRET_KEY") ||
raise("S3_SECRET_KEY is required when STORAGE_BACKEND=s3")
]
else
[]
end
config :my_app, :s3_config, s3_config
This lets release tasks that do not use S3 start without S3 credentials while
still failing fast when S3 is selected.
def call(%{path_info: ["health", "readiness"]} = conn, _opts) do
case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
{:ok, _} -> send_resp(conn, 200, ~s({"status":"ok"})) |> halt()
{:error, _} -> send_resp(conn, 503, ~s({"status":"error"})) |> halt()
end
end
| Need | Use |
|------|-----|
| Simple, managed | Fly.io |
| Enterprise, existing K8s | Kubernetes |
| Custom infrastructure | Docker + your orchestrator |
| Resource | Recommendation |
|----------|----------------|
| CPU | NO LIMITS (BEAM scheduler issues) |
| Memory | Set limits (256Mi-512Mi typical) |
| Graceful shutdown | ≥ 60 seconds |
server: true in endpoint configPhoenix 1.8 uses esbuild + tailwind (no Node.js required):
config/config.exs under :esbuild and :tailwindmix assets.deploy builds for productionmix assets.setup installs binaries on first runconfig/config.exsFor detailed patterns, see:
references/docker-config.md - Multi-stage Dockerfile, best practicesreferences/flyio-config.md - fly.toml, clustering, commandsTake oliver-kriska/claude-elixir-phoenix-pi-deploy from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.