Ecologia

O DeployAlly organiza serviços em um ecossistema com seis archetypes — um enum fechado que descreve a natureza de cada container. O archetype determina defaults de rede, healthcheck, escopo e exposição. Acima dos archetypes existem dois mecanismos de relação entre serviços: needs (consumir um asset) e provides (oferecer functions a consumidores).

Os Seis Archetypes

Todo template declara um — e apenas um — desses valores em identity.archetype.

identity:
  archetype: application  # | asset | static | worker | multi_component_saas | network_appliance

1. `application`

App único com rota HTTP. É o caso padrão para CMSs, painéis, ferramentas web e back-ends.

Aspecto Default
Network public (acessível pelo Traefik)
Healthcheck external_http
Scope shared (várias instances possíveis no mesmo host)
Routes uma rota HTTP na primeira porta interna declarada

Exemplo: Memos, WordPress, n8n, Vaultwarden.

identity:
  slug: memos
  archetype: application
routes:
  - { id: web, kind: http, port: 5230, host: ${input.WEB_HOSTNAME} }

2. `asset`

Serviço de infraestrutura compartilhado — bancos, caches, filas. Singleton por host: um único MariaDB serve N WordPress, n8n, Mautic. Assets têm rede dupla: uma rede dedicada (mariadb) onde consumidores se ligam, e a rede public para que ferramentas administrativas (Adminer, dbgate) também os alcancem.

Aspecto Default
Network [<slug>, public] (dedicada + pública)
Healthcheck external_tcp na porta declarada
Scope singleton
Routes vazio (sem ingress HTTP)

Assets expõem functions via provides.functions para que consumer apps possam provisionar recursos isolados em tempo de deploy.

Exemplo: MariaDB, MySQL, PostgreSQL, Redis.

identity:
  slug: mariadb
  archetype: asset
  scope: singleton
routes: []
provides:
  exports:
    - { from: container.name, to: host }
    - { from: secrets.MARIADB_ROOT_PASSWORD, to: admin_credentials }
  functions:
    create_user_and_db:
      params:
        db_name:  { type: identifier, required: true }
        user:     { type: identifier, required: true }
        password: { type: secret, required: true }
      command:
        kind: docker_exec
        target: ${self.container.name}
        argv: [mariadb, -uroot, -p${self.admin_credentials}, -e,
               "CREATE DATABASE IF NOT EXISTS `${db_name}`; …"]

3. `static`

Site estático servido por nginx ou caddy. Sem estado, sem needs, sem secrets. O mais simples dos archetypes.

Aspecto Default
Network public
Healthcheck external_http em /
Scope shared
Routes uma rota HTTP em /

Exemplo: landing pages, docs estáticas, sites Jekyll/Hugo.

4. `worker`

Processo headless — sem porta HTTP, sem rota. Roda em background processando filas, jobs ou cron. Como não há porta para sondar, o healthcheck observa o estado do processo.

Aspecto Default
Network public (para alcançar APIs externas)
Healthcheck stable_state — settle de 30s, container deve permanecer running
Scope shared
Routes vazio

Workers exigem lifecycle.post_deploy.assert_stable_state por padrão — o deploy só é considerado bem-sucedido se o processo não cair em 30s.

Exemplo: workers de Postal, processadores de fila do n8n, cron containers.

5. `multi_component_saas`

App com múltiplos containers que compõem um único serviço — tipicamente um web + um ou mais workers + dependências auxiliares. Cada componente vira um container separado, mas o conjunto é deployado/decomissionado como unidade.

Aspecto Default
Network public (+ networks adicionais conforme needs)
Healthcheck por componente
Scope shared
Routes referenciam o componente por component: <id>
identity:
  archetype: multi_component_saas
components:
  - id: web
    primary: true
    image: postal/postal:3.3.4
    command: [postal, web-server]
  - id: worker
    image: postal/postal:3.3.4
    command: [postal, worker]
routes:
  - id: web
    kind: http
    component: web
    port: 5000
    host: ${input.WEB_HOSTNAME}

Exemplo: Postal (web + worker + SMTP server), Chatwoot (web + sidekiq).

6. `network_appliance`

Proxy ou ingress de rede. Roda em network_mode: host (precisa enxergar todas as portas e netfilter), é singleton e geralmente não tem rota HTTP exposta — ele é a rota.

Aspecto Default
Network host (sem isolamento de rede)
Healthcheck exit (CLI tools) ou external_tcp
Scope singleton
Routes TCP/UDP brutos quando aplicável

Exemplo: Traefik (ingress de toda a stack DeployAlly), proxies SOCKS, gateways VPN.

Relação entre Archetypes: `needs` e `provides`

Aplicações consomem assets via needs. O bloco é declarativo — descreve o que a app precisa, sem hardcode de credenciais ou hosts.

# wordpress.yaml
needs:
  database:
    class: relational
    options: [mariadb, mysql]
    per_instance:
      database: "wp_${instance.uid}"
      user:     "wp_${instance.uid}"
      password: ${secrets.WP_DB_PASSWORD}
      on_provision:    create_user_and_db
      on_decommission: drop_user_and_db
    exports:
      - { from: container.name, to: WORDPRESS_DB_HOST }

No deploy do WordPress:

  1. O asset dispatcher procura um asset com class: relational rodando no host (label ccs.systems/asset_class=relational).
  2. Achado o asset, dispara a function declarada em on_provision (no caso, create_user_and_db da MariaDB), passando db_name=wp_<uid>, user=wp_<uid>, password=<secret>.
  3. O asset executa a function via docker exec (cria DB + user + grants).
  4. Os exports do asset (container.name → WORDPRESS_DB_HOST) viram env vars do WordPress.
  5. O WordPress sobe, conecta no wp_<uid> isolado.

No decommission, o ciclo inverso: drop_user_and_db é chamado, removendo o DB e o user. A MariaDB compartilhada continua de pé.

Cascade auto-provision

Se o asset não existe no host, o flag --provision-missing-assets instrui o cascade a criá-lo automaticamente — primeira opção de needs.options, variant default: true, mesmo profile do consumer. Há retry com backoff (3→15s, 5 tentativas) para esperar o cold-start do asset.

deployally deploy --species wordpress --instance-uid wp-blog-001 \
  --input WEB_HOSTNAME=blog.example.com \
  --provision-missing-assets \
  --apply

Networks

O DeployAlly cria networks Docker automaticamente conforme o archetype:

Archetype Networks
application public + dedicadas dos assets em needs
asset <slug> (dedicada) + public
static public
worker public + dedicadas dos assets em needs
multi_component_saas public + dedicadas
network_appliance host

Service discovery interno: containers se acham por nome do container. Um WordPress conecta em mariadb-shared:3306 via DNS interno da rede mariadb.

Exemplo Completo

Stack típica: Traefik + MariaDB + Redis + WordPress + Adminer.

┌─────────────────────────────────┐
│ Traefik   (network_appliance)   │  host network, singleton
│ ingress + TLS                   │
└────────────────┬────────────────┘
                 │ public
   ┌─────────────┼─────────────┐
   ▼             ▼             ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│WordPress │ │ Adminer  │ │ … apps   │
│ (app)    │ │ (app)    │ │          │
└────┬─────┘ └────┬─────┘ └──────────┘
     │            │
     │ mariadb    │ mariadb
     ▼            ▼
   ┌─────────────────┐    ┌────────────┐
   │ MariaDB (asset) │    │ Redis      │
   │ singleton       │    │ (asset)    │
   └─────────────────┘    └────────────┘

Ordem de deploy implícita pelo archetype:

  1. network_appliance (Traefik) — singleton, precisa estar no ar antes.
  2. asset (MariaDB, Redis) — singletons que provem functions.
  3. application (WordPress, Adminer) — consomem assets via needs.

Próximos Passos

  • Templates — Como cada bloco (image, routes, storage, etc.) varia por archetype.
  • Definitions e Instances — Como per_instance provisiona recursos isolados.
  • Taxonomia — Como kingdom/family/species organizam o catálogo.
By Borlot.com.br on 05/06/2026