Templates and stacks
Create several connected services in one request, from the template gallery or your own spec.
Templates from the gallery
/containers/templates?search= and ?category= filter the list.{
"success": true,
"data": [
{ "slug": "wordpress", "name": "WordPress", "description": "…", "category": "CMS", "verified": true, "services": ["WordPress", "MySQL"], "deployCount": 128 }
]
}Browse them with screenshots and descriptions at helipod.io/templates.
/containers/environments/{environmentId}/templates/{slug}charges your walletIdempotency-KeyCreates every service of the template in the environment and deploys them, dependencies first.
curl -X POST https://api.helipod.io/api/containers/environments/$ENV/templates/wordpress \
-H "Authorization: Bearer hel_your_key" -H "Content-Type: application/json" \
-d '{ "inputs": {} }'inputs fills the questions a template asks (an admin email, a registry token, ...). Required inputs that are missing are refused with the list of what to fill in. The response (201) is { "services": [ { "service": { ... }, "deploymentId": "..." } ] }.
Stacks: your own spec
/containers/environments/{environmentId}/stackscharges your walletdryRunIdempotency-KeyCreates the services of a template spec, wired together, and deploys them.
A stack uses the same format as Helipod templates. Services reference each other's variables, and get deployed in dependency order.
{
"spec": {
"version": 1,
"inputs": [{ "key": "ADMIN_EMAIL", "label": "Admin email", "required": true }],
"services": [
{
"key": "db",
"name": "Postgres",
"source": { "image": "postgres:16-alpine" },
"variables": {
"POSTGRES_USER": "app",
"POSTGRES_PASSWORD": "${{secret(32)}}",
"POSTGRES_DB": "app",
"DATABASE_URL": "postgres://app:${{POSTGRES_PASSWORD}}@${{HELIPOD_PRIVATE_DOMAIN}}:5432/app"
},
"networking": { "tcp": [{ "port": 5432 }] },
"volumes": [{ "mountPath": "/var/lib/postgresql/data", "sizeGb": 5 }]
},
{
"key": "app",
"name": "App",
"source": { "repo": "https://github.com/acme/app", "branch": "main" },
"variables": { "DATABASE_URL": "${{Postgres.DATABASE_URL}}", "ADMIN_EMAIL": "${{input.ADMIN_EMAIL}}" },
"networking": { "http": [{ "port": 3000, "public": true }] },
"healthcheck": { "path": "/health" },
"dependsOn": ["db"]
}
]
},
"inputs": { "ADMIN_EMAIL": "[email protected]" }
}Prop
Type
Variable values can use:
${{input.KEY}}for an input,${{secret(32)}}and${{uuid}}for a random value generated once,${{Service.KEY}}for a variable of another service in the stack (or already in the environment),${{KEY}}for another variable of the same service,- values Helipod provides:
${{PORT}},${{HELIPOD_PRIVATE_DOMAIN}}(host name inside the environment),${{HELIPOD_PUBLIC_DOMAIN}},${{HELIPOD_TCP_PROXY_DOMAIN}}and${{HELIPOD_TCP_PROXY_PORT}}(public TCP address),${{HELIPOD_SERVICE_NAME}}and${{HELIPOD_ENVIRONMENT_NAME}}. Each also works on another service:${{Postgres.HELIPOD_PRIVATE_DOMAIN}}.
For a private image, source.auth must reference inputs ({ "username": "${{input.REGISTRY_USER}}", "password": "${{input.REGISTRY_TOKEN}}" }) so credentials are never part of the spec itself.
Add ?dryRun=true to get the daily cost of every service without creating anything. Invalid specs are refused with 400 INVALID_SPEC and the first problem found.