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.txt — fastapi, 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: Gateway:

delonix net tunnel expose 8080 --name delonix-temp
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 delete gateway 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

Full project: Delonix Temp

From zero to a service on the public internet — build, run, and a real URL, in 4 commands. Everything in this guide was actually run; the output is real, copied from the run.

A real-time API in FastAPI — looks up the CURRENT temperature of any city (via Open-Meteo, no API key) and serves a page that refreshes itself every 30s. The goal is to walk the full Delonix cycle in one small project: multi-stage build → container run → expose to the internet with tunnel. The complete files are in examples/delonix-temp/ — git clone it and follow the steps below exactly as they are.

1. The app

Three files. main.py — two routes: /api/weather/{city} (geocodes the city, asks for the current temperature, returns JSON) and / (an HTML page that calls the API itself via fetch and redoes it every 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 with a <script> that fetch("/api/weather/"+city) every 30s

(full version, with the HTML page and /health, in the repo's real file.)

requirements.txt — fastapi, uvicorn[standard], httpx, with pinned versions.

Delonixfile — a multi-stage build: one stage installs the Python dependencies, the other just copies the result plus the code — the final image carries no pip cache and no build tools:

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

The ID at the end (ef708d73f029) is the image. delonix image ls confirms the size — the final stage, with no build tools, ends up much smaller than if everything were in a single FROM:

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

3. Run it

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"}

A REAL temperature, looked up live — not a fixed value. The container's logs confirm the requests:

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. Expose it to the internet

A local port isn't enough — the goal is a URL anyone, on any network, can open. This is where kind: Gateway comes in:

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

That URL is REAL — it's the one this guide got when the command was actually run. (Yours will be different every time: the free provider assigns a new one each session.) Confirmation, from the outside, without touching anything 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"}

The same JSON, this time arriving from outside the machine, through an SSH tunnel to a public server (pinggy) and back — zero router configuration, zero public IP of your own, zero account. Opening the URL in a browser shows the Delonix Temp page updating itself.

Going further: with more than one service, put kind: HTTPRoute in front (routing by Host/path to several containers) and point tunnel at the PROXY'S PORT instead of directly at the container — one public URL, as many backends as you need. See examples/httproute.yaml + examples/tunnel.yaml.

Cleanup

delonix delete gateway delonix-temp
delonix container rm -f delonix-temp

What this proved

CommandWhat it validated
delonix builda real multi-stage build (2 stages, COPY --from), with build networking
delonix container run -puserspace NAT with no root, port published on the host
delonix container logsobservability of a real service running
delonix net tunnel exposeREAL public internet traffic reaching a local container, no account, no public IP