Skip to content

Pre-Flight Checklist

Run this before the first meridian deploy for a new project or host. It is not a replacement for meridian check; it covers things Meridian cannot fully know from your local filesystem, DNS provider, Containerfile, or app code.

Examples use my-app, prod-01.example.com, my-app.example.com, and assets.my-app.example.com. Replace them with your service, host, and domains.

Infrastructure

CheckVerifyFailure you avoid
proxy.host resolves to the serverdig +short my-app.example.comLets Encrypt hangs or fails because the public hostname points somewhere else.
assets.host resolves to the server, if assets: is configureddig +short assets.my-app.example.comAsset HTTPS issuance fails even though the app domain works.
Server architecture matches your image platformssh deploy@prod-01.example.com 'uname -m' and `podman image inspect ghcr.io/acme/my-app:latestrg '"Architecture"'`
SSH key exists locally and the agent can use ittest -f /Users/me/.ssh/id_ed25519 && ssh-add -lBootstrap, setup, or deploy fails before any Podman work starts.
ssh.keys uses an absolute pathrg -n '^\s+- /' .meridian/deploy.ymlA relative key path works from one directory and fails from another.
Local Podman has enough disk spacepodman system dfpodman build, podman save, or incremental export fails with no space left.

If the local Podman cache is too large:

bash
podman system prune -af
podman system df

App Code

CheckVerifyFailure you avoid
Health route exists and returns 200 without a database callcurl -i http://localhost:8000/healthDeploy reaches the healthcheck phase and times out.
Production settings read every required environment variable`rg -n 'ENV.(fetch[)' config src`
App listens on every interface, not only localhostpodman run --rm -p 127.0.0.1:8000:8000 ghcr.io/acme/my-app:latest then curl -i http://127.0.0.1:8000/healthMeridian's temporary probe container cannot reach the app container.
App port matches servers.web.proxy.app_port`rg -n 'app_portPORT

For Marten apps, define a cheap /health route yourself and make production bind to 0.0.0.0:<app_port>.

Containerfile

CheckVerifyFailure you avoid
Image is built with the tag in .meridian/deploy.ymlpodman image exists ghcr.io/acme/my-app:latesttransfer.mode: stream or incremental fails before upload.
Image architecture matches the server`podman image inspect ghcr.io/acme/my-app:latestrg '"Os"
Registry-free transfer has a local Podman imagemeridian checkMissing local image is caught before any remote mutation.
Build-time commands have dummy env vars when the framework validates settings`rg -n 'ENV.(fetch[)

If the image is missing locally:

bash
podman build --platform linux/arm64 -t ghcr.io/acme/my-app:latest .
podman image exists ghcr.io/acme/my-app:latest
meridian check

For the failure shape, see image not known during stream or incremental transfer.

deploy.yml

CheckVerifyFailure you avoid
ssh.keys is absoluterg -n '^\s+- /' .meridian/deploy.ymlSSH auth depends on the current working directory.
servers.web.proxy.app_port matches the container portmeridian plan and rg -n 'app_port' .meridian/deploy.ymlHealthcheck and proxy target the wrong port.
servers.web.proxy.ssl: true is only set after DNS is livedig +short my-app.example.comLets Encrypt issuance hangs during deploy.
Each accessory declares host:rg -n '^\s+host:' .meridian/deploy.ymlAccessory commands cannot decide which host to mutate.
Same-host services use per-service env and secret prefixes`rg -n 'DATABASESECRET
Accessories on the app network have readiness probes or inferable portsmeridian planThe app starts before Postgres, Redis, or another accessory is reachable.

Start accessories before the first app deploy:

bash
meridian accessory start postgres
meridian accessory start dragonfly
meridian check

Copy This Into Your Wiki

markdown
# Meridian pre-flight checklist

## Infrastructure
- [ ] `dig +short my-app.example.com` returns the server IP.
- [ ] `dig +short assets.my-app.example.com` returns the server IP, if `assets:` is configured.
- [ ] `ssh deploy@prod-01.example.com 'uname -m'` matches the image platform.
- [ ] `test -f /Users/me/.ssh/id_ed25519 && ssh-add -l` succeeds.
- [ ] `.meridian/deploy.yml` uses absolute paths under `ssh.keys`.
- [ ] `podman system df` shows enough local disk space.

## App code
- [ ] `/health` exists and returns 200 without database access.
- [ ] Production settings read only env vars declared in `.meridian/deploy.yml`.
- [ ] The app listens on `0.0.0.0:<app_port>`.
- [ ] The app's runtime port matches `servers.web.proxy.app_port`.

## Containerfile
- [ ] `podman image exists ghcr.io/acme/my-app:latest` succeeds.
- [ ] The image was built for the server architecture.
- [ ] `stream` or `incremental` transfer has a local Podman image.
- [ ] Build-time framework commands have dummy env vars if settings are validated during build.

## deploy.yml
- [ ] `ssh.keys` paths are absolute.
- [ ] `proxy.app_port` matches the app container port.
- [ ] `proxy.ssl: true` is used only after DNS is live.
- [ ] Every accessory has an explicit `host:`.
- [ ] Env and secret names are prefixed per service for same-host deployments.
- [ ] Accessories are started before first deploy.
- [ ] `meridian check` passes.

MIT License