{"slug":"deploy-2","title":"deploy","summary":"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.","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-10-04T15:14:11.066726Z","repo":{"url":"https://github.com/oliver-kriska/claude-elixir-phoenix","stars":560,"forks":44,"license":"MIT","updatedAt":"2026-10-02T04:11:38Z"},"bodyHtml":"<hr>\n<p>name: deploy\ndescription: \"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.\"\neffort: medium\npaths:</p>\n<ul>\n<li>\"config/runtime.exs\"</li>\n<li>\"Dockerfile\"</li>\n<li>\"fly.toml\"</li>\n<li>\"rel/**/*\"</li>\n</ul>\n<hr>\n<h1>Elixir/Phoenix Deployment Reference</h1>\n<p>Quick reference for deploying Elixir/Phoenix applications.</p>\n<h2>Iron Laws — Never Violate These</h2>\n<ol>\n<li><strong>Config at runtime, not compile time</strong> — Secrets in <code>config.exs</code> get baked into the release binary. Use <code>runtime.exs</code> with env vars so secrets are resolved at boot</li>\n<li><strong>Graceful shutdown ≥ 60 seconds</strong> — Shorter timeouts kill in-flight requests and WebSocket connections mid-operation, causing data loss for users</li>\n<li><strong>Health checks required</strong> — Without startup/liveness/readiness endpoints, orchestrators can't distinguish a booting node from a dead one, leading to cascading restarts</li>\n<li><strong>SSL verification for database</strong> — Skipping <code>verify: :verify_peer</code> allows MITM attacks between your app and database; production data traverses the connection</li>\n<li><strong>No CPU limits</strong> — The BEAM scheduler assumes it owns all cores; cgroups CPU limits cause scheduler collapse where the VM thinks it has more cores than it can use, leading to latency spikes</li>\n<li><strong>Guard optional service credentials</strong> — <code>runtime.exs</code> runs whenever a\nrelease boots, including <code>eval</code>-based migration commands. Only require S3,\nRedis, and similar credentials when that integration is enabled</li>\n</ol>\n<h2>Quick Configuration</h2>\n<h3>runtime.exs (Essential)</h3>\n<pre><code>if config_env() == :prod do\n  database_url = System.get_env(\"DATABASE_URL\") || raise \"DATABASE_URL is required\"\n  secret_key_base = System.get_env(\"SECRET_KEY_BASE\") || raise \"SECRET_KEY_BASE is required\"\n  host = System.get_env(\"PHX_HOST\") || raise \"PHX_HOST is required\"\n\n  config :my_app, MyApp.Repo,\n    url: database_url,\n    pool_size: String.to_integer(System.get_env(\"POOL_SIZE\") || \"10\"),\n    ssl: true,\n    ssl_opts: [verify: :verify_peer]\n\n  config :my_app, MyAppWeb.Endpoint,\n    url: [host: host, port: 443, scheme: \"https\"],\n    http: [ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env(\"PORT\") || \"4000\")],\n    secret_key_base: secret_key_base,\n    server: true\nend\n</code></pre>\n<h3>Guard Optional Services</h3>\n<p>Keep core boot secrets such as <code>DATABASE_URL</code> and <code>SECRET_KEY_BASE</code> required.\nGate credentials for optional integrations behind the same feature switch that\nenables the integration:</p>\n<pre><code>s3_config =\n  if System.get_env(\"STORAGE_BACKEND\") == \"s3\" do\n    [\n      access_key_id:\n        System.get_env(\"S3_ACCESS_KEY\") ||\n          raise(\"S3_ACCESS_KEY is required when STORAGE_BACKEND=s3\"),\n      secret_access_key:\n        System.get_env(\"S3_SECRET_KEY\") ||\n          raise(\"S3_SECRET_KEY is required when STORAGE_BACKEND=s3\")\n    ]\n  else\n    []\n  end\n\nconfig :my_app, :s3_config, s3_config\n</code></pre>\n<p>This lets release tasks that do not use S3 start without S3 credentials while\nstill failing fast when S3 is selected.</p>\n<h3>Health Check Plug</h3>\n<pre><code>def call(%{path_info: [\"health\", \"readiness\"]} = conn, _opts) do\n  case Ecto.Adapters.SQL.query(MyApp.Repo, \"SELECT 1\", []) do\n    {:ok, _} -&gt; send_resp(conn, 200, ~s({\"status\":\"ok\"})) |&gt; halt()\n    {:error, _} -&gt; send_resp(conn, 503, ~s({\"status\":\"error\"})) |&gt; halt()\n  end\nend\n</code></pre>\n<h2>Quick Decisions</h2>\n<h3>Platform Choice</h3>\n<table>\n<thead>\n<tr>\n<th>Need</th>\n<th>Use</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Simple, managed</td>\n<td>Fly.io</td>\n</tr>\n<tr>\n<td>Enterprise, existing K8s</td>\n<td>Kubernetes</td>\n</tr>\n<tr>\n<td>Custom infrastructure</td>\n<td>Docker + your orchestrator</td>\n</tr>\n</tbody>\n</table>\n<h3>Resource Limits</h3>\n<table>\n<thead>\n<tr>\n<th>Resource</th>\n<th>Recommendation</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>CPU</td>\n<td><strong>NO LIMITS</strong> (BEAM scheduler issues)</td>\n</tr>\n<tr>\n<td>Memory</td>\n<td>Set limits (256Mi-512Mi typical)</td>\n</tr>\n<tr>\n<td>Graceful shutdown</td>\n<td>≥ 60 seconds</td>\n</tr>\n</tbody>\n</table>\n<h2>Deployment Checklist</h2>\n<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\"> All secrets from environment variables in runtime.exs</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> Optional service credentials required only when their integration is enabled</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> <code>server: true</code> in endpoint config</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> SSL verification for database connections</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> Health endpoints: /health/startup, /health/liveness, /health/readiness</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> Graceful shutdown period ≥ 60 seconds</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> No CPU limits (memory limits only)</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> Migrations in deploy process</li>\n</ul>\n<h2>Asset Pipeline Notes</h2>\n<p>Phoenix 1.8 uses esbuild + tailwind (no Node.js required):</p>\n<ul>\n<li>Config in <code>config/config.exs</code> under <code>:esbuild</code> and <code>:tailwind</code></li>\n<li><code>mix assets.deploy</code> builds for production</li>\n<li><code>mix assets.setup</code> installs binaries on first run</li>\n<li>Custom JS bundlers: configure in <code>config/config.exs</code></li>\n</ul>\n<h2>References</h2>\n<p>For detailed patterns, see:</p>\n<ul>\n<li><code>${CLAUDE_SKILL_DIR}/references/docker-config.md</code> - Multi-stage Dockerfile, best practices</li>\n<li><code>${CLAUDE_SKILL_DIR}/references/flyio-config.md</code> - fly.toml, clustering, commands</li>\n</ul>\n","files":[{"path":"references/docker-config.md","sizeBytes":2989,"isText":true},{"path":"references/flyio-config.md","sizeBytes":1652,"isText":true},{"path":"SKILL.md","sizeBytes":4750,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-10-04T15:14:42.900167Z","sha256":"14FC65728D23F53DD5EC0938264A45819F59FA6996983310FC61259CE2523BC7","sizeBytes":4891},"review":null,"source":{"repositoryUrl":"https://github.com/oliver-kriska/claude-elixir-phoenix","path":"plugins/elixir-phoenix/skills/deploy","license":"MIT","commit":"9767a82d24ddddad553e85f88efc2869a7fd7d88","subtreeSha":"5968D025467E7A87F4C7DB1E528666DA5B2048AFB7565D8AF433148D72AD2B04","lastSyncedAt":"2026-10-04T15:14:09.139242Z"},"reviewedAt":"2026-10-04T15:15:17.547951Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/plugins/elixir-phoenix/skills/deploy"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart"},{"target":"git","command":"git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git"}]}