Projecto completo: Delonix Temp

Do zero a um serviço na internet pública — build, run, e uma URL real, em 4 comandos. Tudo neste guia foi corrido a sério; o output é real, copiado da execução.

Uma API de tempo real em FastAPI — consulta a temperatura ACTUAL de qualquer cidade (via Open-Meteo, sem API key) e serve uma página que actualiza sozinha a cada 30s. O objectivo é percorrer o ciclo completo do Delonix num só projecto pequeno: build multi-stage → container run → expor à internet com tunnel. Os ficheiros completos estão em examples/delonix-temp/git clone e segue os passos abaixo tal como estão.

1. A app

Três ficheiros. main.py — duas rotas: /api/weather/{city} (geocodifica a cidade, pede a temperatura actual, devolve JSON) e / (uma página HTML que chama a API própria via fetch e refaz a cada 30s):

from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
import httpx

app = FastAPI(title="Delonix Temp")

@app.get("/api/weather/{city}")
async def weather(city: str):
    async with httpx.AsyncClient(timeout=8.0) as client:
        geo = await client.get("https://geocoding-api.open-meteo.com/v1/search",
                                params={"name": city, "count": 1})
        place = geo.json()["results"][0]
        fc = await client.get("https://api.open-meteo.com/v1/forecast", params={
            "latitude": place["latitude"], "longitude": place["longitude"],
            "current": "temperature_2m,weather_code",
        })
    current = fc.json()["current"]
    return {"city": place["name"], "country": place.get("country"),
            "temperature_c": current["temperature_2m"], "observed_at": current["time"]}

@app.get("/", response_class=HTMLResponse)
async def index():
    return PAGE  # HTML com um <script> que faz fetch("/api/weather/"+city) a cada 30s

(versão completa, com a página HTML e /health, no ficheiro real do repo.)

requirements.txtfastapi, uvicorn[standard], httpx, com versões fixas.

Delonixfile — build multi-stage: um estágio instala as dependências Python, o outro só copia o resultado + o código — a imagem final não carrega o cache do pip nem ferramentas de build:

FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY main.py .
ENV PYTHONUNBUFFERED=1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

2. Build

cd examples/delonix-temp
delonix build -t delonix-temp:1 .
Collecting fastapi==0.115.0 (from -r requirements.txt (line 1))
...
Successfully installed annotated-types-0.7.0 anyio-4.14.2 ... fastapi-0.115.0 ...
ef708d73f029

O ID no fim (ef708d73f029) é a imagem. delonix image ls confirma o tamanho — o estágio final, sem as ferramentas de build, fica bem mais pequeno que se fosse tudo num único FROM:

delonix image ls
REPOSITORY:TAG     IMAGE ID       CREATED          SIZE
delonix-temp:1     ef708d73f029   agora mesmo      157.2 MiB
python:3.12-slim   25c5b8011a34   agora mesmo       41.2 MiB

3. Correr

delonix container run -d --name delonix-temp -p 8080:80 delonix-temp:1
curl -s http://localhost:8080/api/weather/Luanda
{"city":"Luanda","country":"Angola","temperature_c":20.2,"observed_at":"2026-07-23T19:00"}

Temperatura REAL, consultada ao vivo — não é um valor fixo. Os logs do container confirmam os pedidos:

delonix container logs delonix-temp
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
INFO:     10.0.2.2:57786 - "GET /health HTTP/1.1" 200 OK
INFO:     10.0.2.2:57802 - "GET /api/weather/Luanda HTTP/1.1" 200 OK

4. Expor à internet

Uma porta local não chega — o objectivo é uma URL que qualquer pessoa, em qualquer rede, consiga abrir. É aqui que entra o kind: Tunnel:

delonix net tunnel expose --name delonix-temp --provider pinggy --local-port 8080
tunnel/delonix-temp: running — https://lfdhz-197-148-40-67.free.pinggy.net

Essa URL é REAL — foi a que este guião recebeu ao correr o comando. (A tua vai ser diferente de cada vez: o provider grátis atribui uma nova de cada sessão.) Confirmação, de fora, sem tocar em nada local:

curl https://lfdhz-197-148-40-67.free.pinggy.net/api/weather/Luanda
{"city":"Luanda","country":"Angola","temperature_c":20.2,"observed_at":"2026-07-23T19:00"}

O mesmo JSON, desta vez a chegar de fora da máquina, por um tunnel SSH até um servidor público (pinggy) e de volta — zero configuração de router, zero IP público próprio, zero conta. Abrir a URL num browser mostra a página Delonix Temp a actualizar-se sozinha.

Ir mais longe: com mais de um serviço, mete o kind: HTTPRoute à frente (routing por Host/path para vários containers) e aponta o tunnel para a PORTA DO PROXY em vez de directamente ao container — uma só URL pública, tantos backends quantos precisares. Ver examples/httproute.yaml + examples/tunnel.yaml.

Arrumar

delonix net tunnel rm delonix-temp
delonix container rm -f delonix-temp

O que isto provou

ComandoO que validou
delonix buildbuild multi-stage real (2 estágios, COPY --from), com rede no build
delonix container run -pNAT userspace sem root, porta publicada no host
delonix container logsobservabilidade de um serviço real a correr
delonix net tunnel exposetráfego REAL da internet pública a chegar a um container local, sem conta nem IP público