A Phoenix + LiveView + Svelte Command Center built on
Caravela, driving the
Library domain (authors, books, publishers) end-to-end —
from domain declaration to generated CRUD to live flows.
These were written directly to scaffold the app:
| Path | Purpose |
|---|---|
| mix.exs | Project definition; depends on caravela from Hex (override via CARAVELA_PATH for local hacking), plus phoenix, live_svelte, ecto_sql, postgrex. Adds ecto.* / assets.* aliases and a Mix release config. |
| .formatter.exs | Imports formatter rules from :ecto, :ecto_sql, and :caravela (so entity / field / relation stay unparenthesised). |
| .gitignore | Standard Elixir ignores plus .env / secrets/ rules. |
| .dockerignore | Keeps _build/, deps/, node_modules/, secrets and markdown out of the Docker build context. |
| config/config.exs | Registers CaravelaDemo.Repo; sets migration primary/foreign keys to :binary_id to match Caravela's generated schemas. |
| config/dev.exs | Local Postgres connection (user postgres, db caravela_demo_dev, port 5432) plus LiveReload and the Vite watcher. |
| config/test.exs | Test Repo config with Ecto.Adapters.SQL.Sandbox. |
| config/prod.exs | Prod compile-time config — static manifest, request-id logger metadata. |
| config/runtime.exs | Reads DATABASE_URL / SECRET_KEY_BASE (plus *_FILE variants for Docker secrets) and forces PHX_HOST at boot. |
| lib/caravela_demo.ex | Top-level module with a short moduledoc. |
| lib/caravela_demo/application.ex | OTP application; supervises Repo, PubSub, FlowRegistry, Caravela.Flow.Supervisor, FlowController, and the Endpoint. |
| lib/caravela_demo/repo.ex | Ecto.Repo for Postgres. |
| lib/caravela_demo/release.ex | Release.migrate/0 called from bin/migrate inside the compiled release. |
| lib/caravela_demo/domains/library.ex | The Caravela domain declaration — entities authors, books, publishers and their relations. |
| lib/caravela_demo_web/controllers/health_controller.ex | /health/live (VM only) and /health/ready (VM + Repo ping). |
| Dockerfile | Multi-stage build: Elixir + Node assets → mix release → slim Debian runtime as non-root app user. |
| rel/env.sh.eex, rel/overlays/bin/server, rel/overlays/bin/migrate | Release overlays — starts the endpoint and runs migrations from inside the release. |
| docker-compose.yaml | Local Postgres 16-alpine for development. |
| docker-stack.yaml | Production stack for Docker Swarm — app + migration one-shot + postgres, with Docker secrets and healthchecks. |
Produced by mix caravela.gen.schema CaravelaDemo.Domains.Library — do
not edit by hand, regenerate instead:
- lib/caravela_demo/library/author.ex
- lib/caravela_demo/library/book.ex
- lib/caravela_demo/library/publisher.ex
priv/repo/migrations/<timestamp>_create_library_tables.exs
# 1. Fetch deps (caravela from Hex, plus phoenix/ecto_sql/postgrex/…)
mix deps.get
# 2. Compile the bare app to make sure the scaffold is valid
mix compile
# 3. Preview what Caravela would generate from the domain
mix caravela.gen.schema CaravelaDemo.Domains.Library --dry-run
# 4. Generate Ecto schemas and the migration for real
mix caravela.gen.schema CaravelaDemo.Domains.Library --force
# 5. Recompile with the generated schemas in place
mix compileHacking on Caravela itself? Point the dep back at a local checkout with
CARAVELA_PATH=../caravela mix deps.get(themix.exshelper respects the env var). Unset it to return to the Hex version.
docker compose up -d # start Postgres
mix ecto.setup # create DB + run the generated migration
mix assets.setup # install the Vite/Svelte JS deps
iex -S mix phx.server # http://localhost:4000To rewrite schemas after editing lib/caravela_demo/domains/library.ex:
mix caravela.gen.schema CaravelaDemo.Domains.Library --force
mix ecto.resetThis repo ships two production artefacts: a Dockerfile that builds
a self-contained Mix release, and a docker-stack.yaml
targeting Docker Swarm. Both are intentionally minimal so they're easy to
adapt to Kamal, Fly, k8s, or plain docker run.
docker build -t ghcr.io/rsousacode/caravela-demo:latest .
docker push ghcr.io/rsousacode/caravela-demo:latestThe build fetches caravela from Hex, compiles assets with Vite, runs
mix release, and copies the release into a slim debian:bookworm-slim
runtime. The final image runs as a non-root user, exposes port 4000, and
boots via tini → /app/bin/server.
config/runtime.exs reads the following at boot:
| Variable | Required | Notes |
|---|---|---|
DATABASE_URL / DATABASE_URL_FILE |
✔ | ecto://USER:PASS@HOST/DB. _FILE form reads from a path (Docker secret). |
SECRET_KEY_BASE / SECRET_KEY_BASE_FILE |
✔ | 64+ bytes — mix phx.gen.secret. |
PHX_HOST |
✔ | Public hostname, e.g. demo.example.com. Used for URL generation and check_origin. |
PHX_SCHEME |
https (default) or http. |
|
PHX_URL_PORT |
External port the browser sees — defaults to 443 / 80. |
|
PORT |
Port the BEAM binds to (default 4000). |
|
POOL_SIZE |
DB pool size (default 10). |
|
DATABASE_SSL |
true to require TLS to Postgres. |
|
ECTO_IPV6 |
true when the DB host only resolves via IPv6. |
|
LOG_LEVEL |
debug / info (default) / warning / error. |
# 1. Create swarm secrets (once, per node group)
printf '%s' "$(mix phx.gen.secret)" | docker secret create caravela_demo_secret_key_base -
printf '%s' 'ecto://app:APP_PASSWORD@postgres/caravela_demo_prod' | docker secret create caravela_demo_database_url -
printf '%s' 'APP_PASSWORD' | docker secret create caravela_demo_postgres_password -
# 2. Point the stack at your image + hostname
export IMAGE=ghcr.io/rsousacode/caravela-demo:latest
export PHX_HOST=demo.example.com
# 3. Deploy
docker stack deploy -c docker-stack.yaml caravela-demo
# 4. Watch the one-shot migration service run to completion
docker service logs -f caravela-demo_migrate
# 5. Tail the app
docker service logs -f caravela-demo_appThe stack exposes the app on ${APP_PORT:-4000} so you can terminate TLS with
Traefik / Caddy / Nginx in front. Uncomment the Traefik labels in
docker-stack.yaml if you're running Traefik inside the
same swarm.
| Path | Checks | Use for |
|---|---|---|
GET /health/live |
BEAM + Endpoint respond | Liveness probe. Don't couple to the DB — you want to keep the container up while you fix the DB. |
GET /health/ready |
Liveness + SELECT 1 against the Repo |
Readiness probe. Gate traffic until the Repo is reachable. |
Yes. It contains no secret values — only references to three external
Docker secrets (caravela_demo_secret_key_base, caravela_demo_database_url,
caravela_demo_postgres_password) that you create on the swarm out of band.
Hostnames and image tags come from env vars (PHX_HOST, IMAGE) at deploy
time, so nothing site-specific is baked into the file either. If you ever
inline a password or a real hostname, move it to a secret or a .env first —
.gitignore already blocks .env*, *.pem, *.key, and secrets/.
MIT — see LICENSE. Builds on Caravela, which is licensed under MPL-2.0.