Skip to content

feat(templates): adding new templates - #125

Merged
gustavobertoi merged 3 commits into
mainfrom
feat/more-templates
Aug 24, 2026
Merged

feat(templates): adding new templates#125
gustavobertoi merged 3 commits into
mainfrom
feat/more-templates

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

What

Grows the built-in catalogue from 22 to 51.

Engines (13): rustfs (S3-compatible), etcd, valkey, mailpit, clickhouse, meilisearch, opensearch, neo4j, timescaledb, keycloak, jaeger, mosquitto, consul.

Apps (16): go.app, rust.app, python.app -> {fastapi, django, flask}, elixir.phoenix, ruby.rails, java.spring, dotnet.aspnet, deno.app, smee, vue.vite, svelte.kit, nuxt, astro.

One template per CONTAINER CONTRACT, not per framework. Go's chi/gin/echo and Rust's axum/actix are libraries: same image, same air/bacon command, so they get one template each rather than five. Python, Elixir and Ruby frameworks genuinely differ in their run command, so those use extends children off a shared parent — the pattern php.nginx -> php.laravel.nginx already established.

Every fact here was verified against the upstream image or docs rather than assumed, which caught a number of things that would have shipped broken:

  • bitnami/etcd is DEAD — subscription-only since Aug 2025, zero public tags. Uses quay.io/coreos/etcd, which publishes no :latest and no floating :v3.6, so the tag must be a full vX.Y.Z.
  • etcd's image is distroless: /bin and /usr/bin are EMPTY. A CMD-SHELL or curl-based healthcheck fails with "exec: sh: not found" forever, so it uses exec-form etcdctl.
  • Setting etcd's listen URLs without advertise URLs is a fatal startup error, and listen URLs must be IP literals (shared-etcd there crashes on boot).
  • ClickHouse with neither CLICKHOUSE_USER nor CLICKHOUSE_PASSWORD set DISABLES network access entirely — the server starts and is simply unreachable.
  • Keycloak ships neither curl nor wget; its healthcheck uses bash /dev/tcp, and port 9000 stays closed unless KC_HEALTH_ENABLED=true.
  • Mailpit's MP_DATABASE is the persistence switch, not the volume: a named volume without it silently stores nothing.
  • Meilisearch's volume must be the /meili_data DIRECTORY, not the .ms file.
  • timescaledb-ha is NOT built on docker-library/postgres — PGDATA lives under /home/postgres/pgdata, so DECISIONS D8's PG18 move does not apply.
  • mosquitto 2.1 already ships a working anonymous config, so no conf mount is needed; its persistence is off by default, so it deliberately has NO volume.
  • jaeger and consul likewise get no volume: both store state in memory in dev mode, and a volume would imply durability neither provides.
  • cargo-watch is unmaintained (its own README redirects to bacon). bacon needs --headless AND -j run-long: plain -j run buffers output until exit, which for a server is never.
  • air moved from cosmtrek to air-verse.
  • redpanda was dropped from the plan on discovering the existing kafka template already IS Redpanda.

Binding is the failure that would bite users hardest — a dev server on 127.0.0.1 inside a container looks healthy, publishes its port, and refuses every connection. uvicorn, flask, rails, vite, astro and nuxt all default to loopback and are explicitly overridden here; TestAppTemplatesBindAllInterfaces asserts it. Phoenix is the exception with no env-var fix: its generated config/dev.exs binds loopback and the template says so in a comment, because only the user can change it.

Wiring: each engine gets its mandatory exposeEngines row (without one an engine is invisible to ports, cannot be exposed and cannot be provisioned) plus a connectionURL case so devstack ports prints something usable.

Tests: new engine lint table with a per-engine assertion on the detail that would silently break it; app-template tests for bind-all-interfaces and for the dependency dirs a bind mount would shadow (deps/_build, obj/bin, node_modules); and two drift tests — every built-in must have a row in BOTH doc tables, and every template dir must be in the go:embed list.

Those drift tests immediately caught pre-existing staleness: templates/README.md had no rows for kafka/nats/rabbitmq/localstack/ministack, and docs/guide/templates.md was missing the mysql/mariadb/mongodb/cassandra/arangodb batch. Both tables are now complete.

gustavobertoi and others added 3 commits August 24, 2026 00:37
…lates

Grows the built-in catalogue from 22 to 51.

Engines (13): rustfs (S3-compatible), etcd, valkey, mailpit, clickhouse,
meilisearch, opensearch, neo4j, timescaledb, keycloak, jaeger, mosquitto,
consul.

Apps (16): go.app, rust.app, python.app -> {fastapi, django, flask},
elixir.phoenix, ruby.rails, java.spring, dotnet.aspnet, deno.app, smee,
vue.vite, svelte.kit, nuxt, astro.

One template per CONTAINER CONTRACT, not per framework. Go's chi/gin/echo and
Rust's axum/actix are libraries: same image, same air/bacon command, so they get
one template each rather than five. Python, Elixir and Ruby frameworks genuinely
differ in their run command, so those use extends children off a shared parent —
the pattern php.nginx -> php.laravel.nginx already established.

Every fact here was verified against the upstream image or docs rather than
assumed, which caught a number of things that would have shipped broken:

* bitnami/etcd is DEAD — subscription-only since Aug 2025, zero public tags.
  Uses quay.io/coreos/etcd, which publishes no :latest and no floating :v3.6,
  so the tag must be a full vX.Y.Z.
* etcd's image is distroless: /bin and /usr/bin are EMPTY. A CMD-SHELL or
  curl-based healthcheck fails with "exec: sh: not found" forever, so it uses
  exec-form etcdctl.
* Setting etcd's listen URLs without advertise URLs is a fatal startup error,
  and listen URLs must be IP literals (shared-etcd there crashes on boot).
* ClickHouse with neither CLICKHOUSE_USER nor CLICKHOUSE_PASSWORD set DISABLES
  network access entirely — the server starts and is simply unreachable.
* Keycloak ships neither curl nor wget; its healthcheck uses bash /dev/tcp, and
  port 9000 stays closed unless KC_HEALTH_ENABLED=true.
* Mailpit's MP_DATABASE is the persistence switch, not the volume: a named
  volume without it silently stores nothing.
* Meilisearch's volume must be the /meili_data DIRECTORY, not the .ms file.
* timescaledb-ha is NOT built on docker-library/postgres — PGDATA lives under
  /home/postgres/pgdata, so DECISIONS D8's PG18 move does not apply.
* mosquitto 2.1 already ships a working anonymous config, so no conf mount is
  needed; its persistence is off by default, so it deliberately has NO volume.
* jaeger and consul likewise get no volume: both store state in memory in dev
  mode, and a volume would imply durability neither provides.
* cargo-watch is unmaintained (its own README redirects to bacon). bacon needs
  --headless AND -j run-long: plain -j run buffers output until exit, which for
  a server is never.
* air moved from cosmtrek to air-verse.
* redpanda was dropped from the plan on discovering the existing kafka template
  already IS Redpanda.

Binding is the failure that would bite users hardest — a dev server on 127.0.0.1
inside a container looks healthy, publishes its port, and refuses every
connection. uvicorn, flask, rails, vite, astro and nuxt all default to loopback
and are explicitly overridden here; TestAppTemplatesBindAllInterfaces asserts it.
Phoenix is the exception with no env-var fix: its generated config/dev.exs binds
loopback and the template says so in a comment, because only the user can change
it.

Wiring: each engine gets its mandatory exposeEngines row (without one an engine
is invisible to `ports`, cannot be exposed and cannot be provisioned) plus a
connectionURL case so `devstack ports` prints something usable.

Tests: new engine lint table with a per-engine assertion on the detail that would
silently break it; app-template tests for bind-all-interfaces and for the
dependency dirs a bind mount would shadow (deps/_build, obj/bin, node_modules);
and two drift tests — every built-in must have a row in BOTH doc tables, and
every template dir must be in the go:embed list.

Those drift tests immediately caught pre-existing staleness: templates/README.md
had no rows for kafka/nats/rabbitmq/localstack/ministack, and
docs/guide/templates.md was missing the mysql/mariadb/mongodb/cassandra/arangodb
batch. Both tables are now complete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The README still described six JS templates and made no mention of the AI-agent
surface that landed in #124.

* Replaces the framework-templates bullet with the real catalogue: apps across
  Go, Rust, Python, Node, Bun, Deno, PHP, Ruby, Elixir, Java and .NET plus the
  Vite family, and a second bullet listing the 25 shared engines.
* Adds the `ai` group to the command table and `config schema` alongside
  `config validate/show`.
* Documents that the whole docs corpus is readable from the binary, and links
  the new AI-agents guide.
* Bumps the spec range to 32 and folds spec 32 into the status paragraph.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit 461f4fd into main Aug 24, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/more-templates branch August 24, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant