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_appliance1. `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:
- O asset dispatcher procura um asset com
class: relationalrodando no host (labelccs.systems/asset_class=relational). - Achado o asset, dispara a function declarada em
on_provision(no caso,create_user_and_dbda MariaDB), passandodb_name=wp_<uid>,user=wp_<uid>,password=<secret>. - O asset executa a function via
docker exec(cria DB + user + grants). - Os exports do asset (
container.name → WORDPRESS_DB_HOST) viram env vars do WordPress. - 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 \
--applyNetworks
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:
network_appliance(Traefik) — singleton, precisa estar no ar antes.asset(MariaDB, Redis) — singletons que provem functions.application(WordPress, Adminer) — consomem assets vianeeds.
Próximos Passos
- Templates — Como cada bloco (image, routes, storage, etc.) varia por archetype.
- Definitions e Instances — Como
per_instanceprovisiona recursos isolados. - Taxonomia — Como kingdom/family/species organizam o catálogo.