▲ Delonix Docs
v0.1.0

Laboratórios

Sessões curtas, do zero a um resultado que se verifica. Cada uma inclui a limpeza — nenhuma deixa nada para trás.

Short sessions, from zero to a verifiable result. Each one includes cleanup — none of them leaves anything behind.

A verificação no fim de cada lab não é decorativa: é o que separa «copiei comandos» de «sei o que isto faz». Onde um passo pode falhar em silêncio, o lab diz exactamente o que olhar.

The verification at the end of each lab isn't decorative: it's what tells apart "I copied commands" from "I know what this does." Where a step could fail silently, the lab tells you exactly what to look at.

Primeiro serviço, em 60 segundosFirst service, in 60 seconds iniciantebeginner

Objectivo: perceber o ciclo run → ps → logs → exec → rm e ver, com rigor, o que «daemonless» quer dizer neste motor.

# 1. Um serviço web, em segundo plano, publicado na porta 8080 do host
delonix container run -d --name web -p 8080:80 nginx:alpine

# 2. Confirmar — o STATUS diz há quanto tempo está de pé
delonix container ps

# 3. O que «daemonless» É: não há um daemon central arrancado no boot.
#    Mas cada recurso tem holders leves — repara em QUEM os pariu (coluna PPID):
ps -o pid,ppid,comm,args -C delonix

# 4. Falar com ele
curl -s localhost:8080 | head -3

# 5. Entrar lá dentro
delonix container exec -it web sh -c 'hostname; id; ls /etc/nginx'

# 6. Ver o que ele escreveu
delonix container logs web | tail -5

# 7. Limpar
delonix container rm -f web

Verificação: o passo 3 mostra alguns processos delonix — tipicamente netns pin, netns control e o holder do próprio run — e o PPID de todos é o systemd --user do teu utilizador, não um serviço do sistema. É esta a diferença de fundo para o Docker: não há um dockerd central a supervisionar tudo; há um holder mínimo por recurso, que morre com o recurso. (Num host tranquilo e sem -p podes nem ver holder nenhum.)

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional (HTTP 200 — não um exit-code isolado), em docs/labs/lab-1/: prepare → run → verify → cleanup (bash docs/labs/lab-1/lab.sh). Baseline histórica — validado em delonix 5.1.1 (Linux, cgroup v2, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: understand the run → ps → logs → exec → rm cycle and see, precisely, what «daemonless» means in this engine.

# 1. A web service, in the background, published on host port 8080
delonix container run -d --name web -p 8080:80 nginx:alpine

# 2. Confirm — STATUS shows how long it's been up
delonix container ps

# 3. What «daemonless» IS: no central daemon is started at boot.
#    But each resource has lightweight holders — note WHO spawned them (PPID):
ps -o pid,ppid,comm,args -C delonix

# 4. Talk to it
curl -s localhost:8080 | head -3

# 5. Go inside
delonix container exec -it web sh -c 'hostname; id; ls /etc/nginx'

# 6. See what it wrote
delonix container logs web | tail -5

# 7. Clean up
delonix container rm -f web

Verification: step 3 shows a few delonix processes — typically netns pin, netns control and the run holder itself — and every one's PPID is your own systemd --user, not a system service. That is the fundamental difference from Docker: there is no central dockerd supervising everything; there's a minimal holder per resource, which dies with the resource. (On a quiet host with no -p you may see no holder at all.)

Reproducible harness (§7): this lab runs on its own, asserting a functional result (HTTP 200 — not an isolated exit code), under docs/labs/lab-1/: prepare → run → verify → cleanup (bash docs/labs/lab-1/lab.sh). Historical baseline — validated on delonix 5.1.1 (Linux, cgroup v2, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

Limites de recursos que pegam mesmoResource limits that actually take iniciantebeginner

Objectivo: descobrir se este host aplica limites — e o que fazer quando não aplica. É o erro nº1 de quem começa em rootless.

# 1. Perguntar ANTES de assumir
delonix system setup

# 2. Se disser INERT ou PARTIAL, a 1.ª correcção não precisa de root nenhum
systemd-run --user --scope -p Delegate=yes -- delonix system setup
#   … e é dentro desse scope que corres os passos seguintes.
#   Só se ELE ainda disser que falta o `cpu`:
#     sudo delonix system setup --delegate   (+ logout / login)

# 3. Um container com tecto de memória
delonix container run -d --name limitado -m 128M alpine sleep 300

# 4. A prova: ler o cgroup REAL, não o que o comando diz
PID=$(delonix container inspect limitado | jq -r .pid)
cat /sys/fs/cgroup$(awk -F: '/^0::/{print $3}' /proc/$PID/cgroup)/memory.max

delonix container rm -f limitado

Verificação: o passo 4 imprime 134217728 (128 MiB), não max. Se imprimir max, a delegação não está feita — e o container corre sem limite nenhum, em silêncio. É por isso que o passo 1 existe.

Nota: cpuset e io aparecem quase sempre como absent, e está certo — num Ubuntu de fábrica o user.slice (da root) só passa cpu memory pids para baixo. Nada aqui precisa deles.

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional — o memory.max REAL do cgroup do kernel, não a saída do comando — e falha em voz alta se o limite não pegou (memory.max=max), em vez de passar em silêncio, em docs/labs/lab-2/: prepare → run → verify → cleanup (bash docs/labs/lab-2/lab.sh). Baseline histórica — validado em delonix 5.1.1 (Linux, cgroup v2, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: find out whether this host enforces limits — and what to do when it doesn't. It's mistake #1 for anyone starting out in rootless.

# 1. Ask BEFORE assuming
delonix system setup

# 2. If it says INERT or PARTIAL, the 1st fix needs no root at all
systemd-run --user --scope -p Delegate=yes -- delonix system setup
#   … and you run the following steps INSIDE that scope.
#   Only if it STILL says `cpu` is missing:
#     sudo delonix system setup --delegate   (+ logout / login)

# 3. A container with a memory ceiling
delonix container run -d --name limited -m 128M alpine sleep 300

# 4. The proof: read the REAL cgroup, not what the command says
PID=$(delonix container inspect limited | jq -r .pid)
cat /sys/fs/cgroup$(awk -F: '/^0::/{print $3}' /proc/$PID/cgroup)/memory.max

delonix container rm -f limited

Verification: step 4 prints 134217728 (128 MiB), not max. If it prints max, delegation isn't set up — and the container runs with no limit at all, silently. That's why step 1 exists.

Note: cpuset and io almost always show up as absent, and that's correct — on a stock Ubuntu, root's user.slice only passes cpu memory pids down. Nothing here needs them.

Reproducible harness (§7): this lab runs on its own, asserting a functional result — the REAL memory.max of the kernel cgroup, not the command's output — and it fails loudly if the limit didn't take (memory.max=max) instead of passing silently, under docs/labs/lab-2/: prepare → run → verify → cleanup (bash docs/labs/lab-2/lab.sh). Historical baseline — validated on delonix 5.1.1 (Linux, cgroup v2, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

Duas aplicações que se falam por nomeTwo apps that talk to each other by name intermédiointermediate

Objectivo: rede de utilizador, DNS interno, e isolamento — sem configurar DNS nenhum.

# 1. Uma rede própria
delonix network create loja

# 2. Base de dados e app, ambas nela
delonix container run -d --name db  --net loja -e POSTGRES_PASSWORD=x postgres:16-alpine
delonix container run -d --name app --net loja -p 8080:80 nginx:alpine

# 3. A app resolve a db PELO NOME, sem /etc/hosts nem variáveis
delonix container exec app sh -c 'getent hosts db; nc -z db 5432 && echo "porta aberta"'

# 4. Fechar a db a tudo menos à app (alcançabilidade DIRIGIDA)
cat > dep.yaml <<'EOF'
apiVersion: delonix.io/v1
kind: Dependency
metadata:
  name: app-conhece-db
spec:
  from: app
  to: db
  ports: ["5432"]
EOF
delonix stack apply -f dep.yaml

# 5. Provar: um terceiro container na MESMA rede já não alcança a db
delonix container run --rm --net loja alpine sh -c 'nc -z -w2 db 5432 || echo BLOQUEADO'

delonix container rm -f db app; delonix network rm loja; rm dep.yaml

Verificação: o passo 3 diz "porta aberta" e o passo 5 diz "BLOQUEADO". A mesma rede, dois resultados — é isso que kind: Dependency faz e uma rede sozinha não faz.

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional — a app resolve a db pelo nome (getent hosts db → IP) e a porta responde ao par autorizado — e falha em voz alta se a db ficar aberta a todos (o terceiro container ainda a alcançar), distinguindo «bloqueado por política» de «serviço em baixo», em vez de passar em silêncio, em docs/labs/lab-3/: prepare → run → verify → cleanup (bash docs/labs/lab-3/lab.sh). Baseline histórica — validado em delonix 5.1.1 (Linux, cgroup v2, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: user network, internal DNS, and isolation — with zero DNS configuration.

# 1. Your own network
delonix network create shop

# 2. Database and app, both on it
delonix container run -d --name db  --net shop -e POSTGRES_PASSWORD=x postgres:16-alpine
delonix container run -d --name app --net shop -p 8080:80 nginx:alpine

# 3. The app resolves db BY NAME, no /etc/hosts, no env vars
delonix container exec app sh -c 'getent hosts db; nc -z db 5432 && echo "port open"'

# 4. Lock db down to everyone except app (DIRECTED reachability)
cat > dep.yaml <<'EOF'
apiVersion: delonix.io/v1
kind: Dependency
metadata:
  name: app-knows-db
spec:
  from: app
  to: db
  ports: ["5432"]
EOF
delonix stack apply -f dep.yaml

# 5. Prove it: a third container on the SAME network can no longer reach db
delonix container run --rm --net shop alpine sh -c 'nc -z -w2 db 5432 || echo BLOCKED'

delonix container rm -f db app; delonix network rm shop; rm dep.yaml

Verification: step 3 says "port open" and step 5 says "BLOCKED". Same network, two different outcomes — that's what kind: Dependency does that a plain network alone doesn't.

Reproducible harness (§7): this lab runs on its own, asserting a functional result — the app resolves db by name (getent hosts db → IP) and the port answers the authorized peer — and it fails loudly if db is left open to everyone (the third container still reaching it), telling "blocked by policy" apart from "service down" instead of passing silently, under docs/labs/lab-3/: prepare → run → verify → cleanup (bash docs/labs/lab-3/lab.sh). Historical baseline — validated on delonix 5.1.1 (Linux, cgroup v2, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

Do Dockerfile à imagem, sem daemonFrom Dockerfile to image, no daemon intermédiointermediate

Objectivo: construir uma imagem e correr o que construíste.

mkdir -p lab-build && cd lab-build
cat > Delonixfile <<'EOF'
FROM alpine:latest
RUN apk add --no-cache curl
COPY ola.txt /ola.txt
HEALTHCHECK CMD test -f /ola.txt
CMD ["sh", "-c", "cat /ola.txt; sleep 300"]
EOF
echo "construído com delonix" > ola.txt

# `Delonixfile` é procurado antes de `Dockerfile` — sem -f nenhum
delonix build -t minha-app:1.0 .

# `--wait` bloqueia até o HEALTHCHECK passar: sem isto escreve-se
# `until ...; do sleep 1; done`, e escreve-se mal
delonix container run -d --name a1 --wait --health-interval 2 minha-app:1.0
delonix container ps

delonix container rm -f a1; delonix image remove minha-app:1.0; cd ..; rm -rf lab-build

Verificação: o ps mostra (healthy) na coluna STATUS. O motor está a sondar o container sozinho — sem systemd, ao contrário do Podman rootless.

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional — lê (healthy) da coluna STATUS de container ps -o json (não só Up), com retry, e prova que delonix build acha o Delonixfile antes de qualquer Dockerfile sem -f — e falha em voz alta distinguindo «build falhou» de «healthcheck nunca passou», em vez de passar em silêncio, em docs/labs/lab-4/: prepare → run → verify → cleanup (bash docs/labs/lab-4/lab.sh). Baseline histórica — validado em delonix 5.1.1 (Linux, cgroup v2, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: build an image and run what you built.

mkdir -p lab-build && cd lab-build
cat > Delonixfile <<'EOF'
FROM alpine:latest
RUN apk add --no-cache curl
COPY hello.txt /hello.txt
HEALTHCHECK CMD test -f /hello.txt
CMD ["sh", "-c", "cat /hello.txt; sleep 300"]
EOF
echo "built with delonix" > hello.txt

# `Delonixfile` is looked for before `Dockerfile` — no -f needed
delonix build -t my-app:1.0 .

# `--wait` blocks until HEALTHCHECK passes: without it you'd have to
# write `until ...; do sleep 1; done` yourself, and get it wrong
delonix container run -d --name a1 --wait --health-interval 2 my-app:1.0
delonix container ps

delonix container rm -f a1; delonix image remove my-app:1.0; cd ..; rm -rf lab-build

Verification: ps shows (healthy) in the STATUS column. The engine is probing the container on its own — no systemd, unlike rootless Podman.

Reproducible harness (§7): this lab runs on its own, asserting a functional result — it reads (healthy) from the STATUS column of container ps -o json (not just Up), with retry, and proves delonix build finds the Delonixfile before any Dockerfile with no -f — and it fails loudly, telling "build failed" apart from "healthcheck never passed" instead of passing silently, under docs/labs/lab-4/: prepare → run → verify → cleanup (bash docs/labs/lab-4/lab.sh). Historical baseline — validated on delonix 5.1.1 (Linux, cgroup v2, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

Uma microVM a sérioA real microVM intermédiointermediate

Objectivo: uma VM completa com o mesmo CLI dos containers.

# 1. Sem imagem local, o create descarrega a oficial sozinho
delonix vm create dev

# 2. Onde ficou, e com que IP
delonix vm ls
delonix describe vms dev

# 3. Entrar (voltar ao host: Ctrl+])
delonix vm console dev

# 4. Checkpoint de sistema — memória E disco, com a VM a correr
delonix vm snapshot create dev limpa
delonix vm snapshot ls dev

# 5. Estragar alguma coisa lá dentro e voltar atrás
delonix vm snapshot restore dev limpa

# Teardown: vm destroy leva o disco, os snapshots E o record (sem órfãos)
delonix vm destroy dev

Verificação: o passo 4 devolve sem erro com a VM A CORRER, e o snapshot fica como um checkpoint de sistema — memória E disco. Com a VM parada seria disk-only (não falha — o vm stop faz undefine do domínio em execução, mas preserva disco, record e snapshots). O teardown é vm destroy, que remove tudo o que a VM possui.

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional — lê de vm ls -o json que a VM fica Running com IPv4, que o snapshot tirado com a VM a correr aparece em snapshot ls e que o restore a mantém de pé, e prova que vm destroy não deixa órfãos (describe vm deixa de resolver) — falha em voz alta distinguindo «VM nunca arrancou» de «sem IP» de «snapshot/restore falhou», em docs/labs/lab-5/: prepare → run → verify → cleanup (bash docs/labs/lab-5/lab.sh). Baseline histórica — validado em delonix 5.1.1 (Linux, libvirt, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: a full VM with the same CLI as containers.

# 1. With no local image, create downloads the official one on its own
delonix vm create dev

# 2. Where it landed, and with what IP
delonix vm ls
delonix describe vms dev

# 3. Go in (back to the host: Ctrl+])
delonix vm console dev

# 4. System checkpoint — memory AND disk, VM still running
delonix vm snapshot create dev clean
delonix vm snapshot ls dev

# 5. Break something inside and roll back
delonix vm snapshot restore dev clean

# Teardown: vm destroy takes the disk, the snapshots AND the record (no orphans)
delonix vm destroy dev

Verification: step 4 returns with no error and the VM STILL RUNNING, and the snapshot is a system checkpoint — memory AND disk. A stopped VM would give a disk-only snapshot (it does not fail — vm stop undefines the running domain but preserves the disk, record and snapshots). The teardown is vm destroy, which removes everything the VM owns.

Reproducible harness (§7): this lab runs on its own, asserting a functional result — it reads from vm ls -o json that the VM reaches Running with an IPv4, that the snapshot taken while running shows up in snapshot ls and that restore keeps it up, and it proves vm destroy leaves no orphans (describe vm stops resolving) — it fails loudly, telling "VM never started" apart from "no IP" apart from "snapshot/restore failed", under docs/labs/lab-5/: prepare → run → verify → cleanup (bash docs/labs/lab-5/lab.sh). Historical baseline — validated on delonix 5.1.1 (Linux, libvirt, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

A tua própria imagem de VM, com VMfileYour own VM image, with a VMfile avançadoadvanced

Objectivo: construir uma imagem qcow2 à tua medida, como se constrói uma imagem de container.

mkdir -p lab-vm && cd lab-vm

# Scaffold que CONSTRÓI COMO ESTÁ — apaga o que não precisares
delonix vm image init --name minha-base
cat VMfile

# Precisa de libguestfs no host: sudo apt install libguestfs-tools
delonix vm image build -t minha-base:1.0 .
delonix vm ls

# Um RUN com `apt-get install` precisa de rede no convidado, e pede-se:
#   delonix vm image build --network -t minha-base:1.0 .

# Arrancar a partir dela
delonix vm create teste --disk minha-base:1.0 --ssh-key @~/.ssh/id_ed25519.pub

# …ou a partir de um qcow2 publicado por ti, sem passar pelo store
delonix vm create outra --url-img https://o-teu-bucket/imagem.qcow2

delonix vm destroy teste; cd ..; rm -rf lab-vm

Verificação: o build imprime [1/1] stage-1: FROM ubuntu:24.04 e verifica o checksum da cloud image. Com --url-img, se não houver <url>.sha256 publicado, o motor diz que está a confiar só no TLS — em vez de calar. A chave que injectas vai para a conta delonix, não para a conta default da distro — o bloco de próximos passos do vm create imprime o ssh exacto.

Arnês reproduzível (§7): este lab corre sozinho, com asserção de resultado funcional — lê de vm image ls -o json que a imagem minha-base:1.0 ficou no store, de vm ls -o json que uma VM arranca Running com IPv4 a partir dela, e prova que vm destroy não deixa órfãos e que a imagem sobrevive ao destroy (o overlay era por-VM) — falha em voz alta distinguindo «imagem não construiu» de «VM não arrancou» de «órfão», em docs/labs/lab-6/: prepare → run → verify → cleanup (bash docs/labs/lab-6/lab.sh). Precisa de libguestfs (virt-customize) no host; o arnês gera a sua própria chave SSH e aponta o SSHKEY do scaffold para ela. Baseline histórica — validado em delonix 5.1.1 (Linux, libguestfs + libvirt, rootless) a 2026-10-24; não re-medido na linha 0.1.0 actual.

Goal: build your own qcow2 image, the same way you build a container image.

mkdir -p lab-vm && cd lab-vm

# Scaffold that BUILDS AS-IS — delete what you don't need
delonix vm image init --name my-base
cat VMfile

# Needs libguestfs on the host: sudo apt install libguestfs-tools
delonix vm image build -t my-base:1.0 .
delonix vm ls

# A RUN with `apt-get install` needs guest networking, so ask for it:
#   delonix vm image build --network -t my-base:1.0 .

# Boot from it
delonix vm create test --disk my-base:1.0 --ssh-key @~/.ssh/id_ed25519.pub

# …or from a qcow2 you published yourself, bypassing the store
delonix vm create other --url-img https://your-bucket/image.qcow2

delonix vm destroy test; cd ..; rm -rf lab-vm

Verification: the build prints [1/1] stage-1: FROM ubuntu:24.04 and verifies the cloud image's checksum. With --url-img, if there's no published <url>.sha256, the engine says it's trusting TLS alone — instead of staying quiet. The key you inject goes to the delonix account, not the distro's default account — the "next steps" block from vm create prints the exact ssh command.

Reproducible harness (§7): this lab runs on its own, asserting a functional result — it reads from vm image ls -o json that the minha-base:1.0 image landed in the store, from vm ls -o json that a VM boots Running with an IPv4 from it, and it proves vm destroy leaves no orphans and that the image survives the destroy (the overlay was per-VM) — it fails loudly, telling "image did not build" apart from "VM never started" apart from "orphan", under docs/labs/lab-6/: prepare → run → verify → cleanup (bash docs/labs/lab-6/lab.sh). Needs libguestfs (virt-customize) on the host; the harness generates its own SSH key and points the scaffold's SSHKEY at it. Historical baseline — validated on delonix 5.1.1 (Linux, libguestfs + libvirt, rootless) on 2026-10-24; not re-measured on the current 0.1.0 line.

Materiais: guia escrito · arnês executável (prepare → run → verify → cleanup).

Materials: written guide · runnable harness (prepare → run → verify → cleanup).

Kubernetes sem DockerKubernetes with no Docker avançadoadvanced

Objectivo: um cluster local cujo runtime dos nós É o Delonix.

# 1. Preflight — falha em milissegundos se faltar a delegação de `cpu`,
#    em vez de descarregar 425 MB para morrer aos 90 segundos
delonix system setup

# 2. O cluster
delonix cluster create --name lab

# 3. Falar com ele
export KUBECONFIG=~/.local/share/delonix/clusters/lab-kubeconfig.yaml
kubectl get nodes -o wide

# 4. Levar uma imagem TUA para dentro dos nós, sem registo nenhum
delonix build -t app:dev .
delonix cluster load app:dev --name lab
kubectl run app --image=app:dev --image-pull-policy=Never

# 5. A prova que interessa
kubectl get pod app -w

delonix delete clusters lab

Verificação: o passo 5 chega a Running. Nem o ctr images import nem o crictl images provam isto — os dois já reportaram sucesso sobre uma imagem que o kubelet depois não resolvia.

Goal: a local cluster whose node runtime IS Delonix.

# 1. Preflight — fails in milliseconds if `cpu` delegation is missing,
#    instead of downloading 425 MB just to die at the 90-second mark
delonix system setup

# 2. The cluster
delonix cluster create --name lab

# 3. Talk to it
export KUBECONFIG=~/.local/share/delonix/clusters/lab-kubeconfig.yaml
kubectl get nodes -o wide

# 4. Get an image of YOURS into the nodes, with no registry at all
delonix build -t app:dev .
delonix cluster load app:dev --name lab
kubectl run app --image=app:dev --image-pull-policy=Never

# 5. The proof that matters
kubectl get pod app -w

delonix delete clusters lab

Verification: step 5 reaches Running. Neither ctr images import nor crictl images prove this — both have reported success on an image the kubelet then couldn't resolve.

IaC de verdade: plan, apply convergente, e a recriação recusadaReal IaC: plan, converging apply, and a recreate that gets refused avançadoadvanced

Objectivo: o ciclo completo — plan → apply → plan outra vez (sem alterações) → mudar um campo QUENTE → mudar um campo FRIO — sem ficheiro de estado nenhum: o que já foi aplicado vive no PRÓPRIO recurso, não num .tfstate à parte.

mkdir -p lab-iac && cd lab-iac
cat > stack.yaml <<'EOF'
apiVersion: delonix.io/v1
kind: Network
metadata:
  name: iac-lab
spec: {}
---
apiVersion: delonix.io/v1
kind: Container
metadata:
  name: iac-app
spec:
  image: nginx:alpine
  network: iac-lab
  memory: 64M
EOF

# 1. Nada foi aplicado ainda — o plano di-lo, e o exit code também
delonix stack plan -f stack.yaml --detailed-exitcode; echo "exit=$?"   # 2 = há alterações

# 2. Aplicar
delonix stack apply -f stack.yaml

# 3. O MESMO plano, sobre o que acabou de ser criado
delonix stack plan -f stack.yaml --detailed-exitcode; echo "exit=$?"   # 0 = nada a fazer

# 4. Mudar um campo QUENTE — o plano propõe uma actualização, não uma recriação
sed -i 's/memory: 64M/memory: 128M/' stack.yaml
delonix stack plan -f stack.yaml     # memory: 64M → 128M
delonix stack apply -f stack.yaml    # "updating 1 field(s) live" — MESMO pid

# 5. Mudar um campo FRIO (a imagem) — o apply RECUSA-SE, não adivinha
sed -i 's/image: nginx:alpine/image: httpd:alpine/' stack.yaml
delonix stack apply -f stack.yaml    # refused: does not converge live: image

# 6. Só com a decisão explícita é que recria
delonix stack apply -f stack.yaml --replace Container/iac-app

delonix container rm -f iac-app; delonix network rm iac-lab; cd ..; rm -rf lab-iac

Verificação: no passo 4, o pid antes e depois (delonix container inspect iac-app | grep pid) é o MESMO — só o cgroup mudou. No passo 5 o apply sai com erro e nada muda — nem o container antigo é tocado — até correres o passo 6 de propósito. É a diferença entre uma ferramenta que converge e uma que só sabe criar: um terraform apply sobre um campo imutável também recria; a diferença aqui é que a recriação nunca é silenciosa, precisa do nome exacto do recurso, e sem ela o comando falha em vez de decidir por ti. Para remover o que o ficheiro deixou de declarar (sem tocar no resto), existe ainda stack apply --prune — não corrido aqui de propósito, para este lab nunca apagar mais do que os dois recursos que ele próprio criou.

Goal: the full cycle — plan → apply → plan again (nothing to do) → change a HOT field → change a COLD field — with no state file at all: what's already applied lives on the resource ITSELF, not in a .tfstate off to the side.

mkdir -p lab-iac && cd lab-iac
cat > stack.yaml <<'EOF'
apiVersion: delonix.io/v1
kind: Network
metadata:
  name: iac-lab
spec: {}
---
apiVersion: delonix.io/v1
kind: Container
metadata:
  name: iac-app
spec:
  image: nginx:alpine
  network: iac-lab
  memory: 64M
EOF

# 1. Nothing applied yet — the plan says so, and so does the exit code
delonix stack plan -f stack.yaml --detailed-exitcode; echo "exit=$?"   # 2 = changes pending

# 2. Apply it
delonix stack apply -f stack.yaml

# 3. The SAME plan, against what was just created
delonix stack plan -f stack.yaml --detailed-exitcode; echo "exit=$?"   # 0 = nothing to do

# 4. Change a HOT field — the plan proposes an update, not a recreate
sed -i 's/memory: 64M/memory: 128M/' stack.yaml
delonix stack plan -f stack.yaml     # memory: 64M → 128M
delonix stack apply -f stack.yaml    # "updating 1 field(s) live" — SAME pid

# 5. Change a COLD field (the image) — apply REFUSES, it doesn't guess
sed -i 's/image: nginx:alpine/image: httpd:alpine/' stack.yaml
delonix stack apply -f stack.yaml    # refused: does not converge live: image

# 6. Only an explicit decision recreates it
delonix stack apply -f stack.yaml --replace Container/iac-app

delonix container rm -f iac-app; delonix network rm iac-lab; cd ..; rm -rf lab-iac

Verification: in step 4, the pid before and after (delonix container inspect iac-app | grep pid) is the SAME — only the cgroup changed. In step 5 apply exits with an error and nothing changes — not even the old container is touched — until you deliberately run step 6. That's the difference between a tool that converges and one that only knows how to create: a terraform apply over an immutable field also recreates; the difference here is that the recreate is never silent, it needs the exact resource name, and without it the command fails instead of deciding for you. To remove what the file no longer declares (without touching anything else), there's also stack apply --prune — not run here on purpose, so this lab never deletes more than the two resources it created itself.

Delonix Engine · Apache-2.0 · ngolacloud/delonix · Referência gerada do --help real do binário por docs/gen.py.Delonix Engine · Apache-2.0 · ngolacloud/delonix · Reference generated from the real --help of the binary by docs/gen.py.