jonnifyelixir
F7 · Deploy to Fly / F7.4 · fly.toml & the deploy mechanics

F7 · the deploy · module 4

One file, one deploy

F7.1 built the image, F7.2 the runtime config, F7.3 the migrated database — each individually sound. A deploy is the act that takes all three to a running machine, and the act needs its own correct artifact: a fly.toml distilled from a full Codemoji production config down to the Portal essentials. No single silent-until-boot gotcha lives here; this is the orchestration layer. It names the app, pins the build, bakes the non-secret [env], sizes the VM, and declares the GET /health check; the two secrets it does not carry — SECRET_KEY_BASE and DATABASE_URL — are provisioned by fly secrets, referenced by name, never committed, and a missing one fails the boot loudly by the runtime.exs contract. One fly deploy rolls the result across the machines, and auto_rollback covers a bad release.

Three dives: the distilled fly.toml walked stanza by stanza, with the de-Codemoji keep-versus-drop; the split between baked [env] and the two provisioned secrets, and the raise-on-missing contract that couples each secret name to a runtime.exs read; and the deploy lifecycle — the rolling release, the machine states, the crash-loop suspend, and the recovery fork.

Distilled to the essentials

The starting point was not an empty file. It was a full Codemoji production fly.toml — one that names a Fastify worker, a Redis queue, a SQLite-stream volume, and a Datadog agent. This single Phoenix release runs none of those, so the config is distilled: every stanza the Portal needs is kept, and every stanza for a service it does not run is dropped. Keeping a stanza for an absent service is not harmless — it points the platform at something that will never answer. Select a config family to see whether the Portal keeps it or the distillation drops it.

Keep versus drop · select a config family

KEEP — the Portal essentials app · [build] · [env] · [experimental] · [[vm]] · [[services]] · /health a service this release runs DROP — de-Codemoji (a service the Portal does not run) Fastify / WORKER_* · Redis / BullMQ · DD_* · SQLitestream / DISTR_WATCH_* · NODE_VERSION removed in the distillation

family: app / region · verdict: keep

The distilled fly.toml carries one Phoenix release and one Postgres, and the template’s memory_mb = 2048 comes down to 1024. A stanza for a service that does not exist is not neutral — it is a health check that can never pass.

Baked env vs provisioned secret

“Configured for prod” has two halves. The non-secret values live in the [env] block of fly.toml, committed in the open: the Portal’s own host, the listener gate, the pool size, the IPv6 trio. The two secrets are not in the file at all — SECRET_KEY_BASE and DATABASE_URL are set out of band with fly secrets set … -a echo-portal and listed by name with fly secrets list. The coupling is exact: every secret name a deploy provides must equal a name runtime.exs reads, or the boot raises. Select a value to see where it lives and what happens when it is missing.

Where a value lives · select one

fly.toml [env] · committed PHX_HOST · PHX_SERVER · MIX_ENV PORT · POOL_SIZE · ECTO_IPV6 ERL_INETRC · ERL_AFLAGS · TZ non-secret · in the open fly secrets · never committed SECRET_KEY_BASE DATABASE_URL set via fly secrets set … -a echo-portal a missing one raises in runtime.exs

A non-secret key is baked into fly.toml; a secret is provisioned by fly secrets and sourced from the Operator’s uncommitted .env.production. The runtime.exs raises enforce presence, not a literal — a missing required secret fails the boot by design.

Three deep dives

In order: the distilled fly.toml stanza by stanza, with the de-Codemoji keep-versus-drop and the /health check coupled to the router’s health route; the [env]-versus-secret split and the raise-on-missing contract; and the rolling fly deploy, the machine lifecycle, the crash-loop suspend, and the release_command open decision.

F7.4.1
The distilled fly.toml
Every stanza walked top to bottom — app/[build]/[deploy]/[experimental]/[[vm]]/[[services]] — the de-Codemoji keep-versus-drop, the memory_mb 2048→1024, and the /health check coupled to the router route.
F7.4.2
Secrets & env provisioning
The baked [env] keys versus the two secrets set by fly secrets; the secret-name to runtime.exs-read coupling, the three raise-on-missing contracts, and .env.production as the Operator’s uncommitted source.
F7.4.3
The deploy lifecycle & failure modes
The rolling fly deploy and auto_rollback; the absent release_command open decision; machine states, the crash-loop suspend-after-ten, and the fly machine restart-versus-fresh-fly deploy recovery fork.

F7.1–F7.3 built the parts

the image, the runtime config, and the migrated database, each individually sound but separately verified.

F7.4 ships them as one

one fly deploy rolls the image, config, and schema across the machines, and auto_rollback reverts a bad release.

Start with the distilled fly.toml, then secrets and the raise contract, then the deploy lifecycle. This is the fourth rung of F7; once a deploy rolls and self-heals, F7.5 verifies the composed result is live and gives the day-2 runbook. It ships F7.3 — the database goes live, whose schema this deploy carries to the machine.

References

The fly.toml stanza reference, the secret-provisioning model, the rolling deploy and its health-check rollback, and the boot-time configuration contract the secrets satisfy.

Sources

Related in this course