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
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
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.
fly.toml 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.[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.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
- Fly.io — fly.toml configuration reference — every stanza this module keeps:
[build],[deploy],[env],[[vm]],[[services]],[[services.http_checks]]. - Fly.io — App secrets —
fly secrets set/list, provisioning a value by name without committing it. - Fly.io — Deploy an app —
fly deploy, the rolling strategy, andauto_rollbackon a failed release. - Fly.io — Health checks — the HTTP checks the edge proxy runs against
/healthto decide a machine is healthy. - Phoenix — Deploying with releases —
runtime.exsand the raise-on-missing-secret contract the provisioned names satisfy.
Related in this course
- F7.4.1 · The distilled fly.toml
- F7.4.2 · Secrets & env provisioning
- F7.4.3 · The deploy lifecycle & failure modes
- F7.3 · The database goes live — the schema this deploy ships to the machine.
- F7 · Deploy to Fly — the chapter overview and the deploy ladder.