Authoring Templates
You can author your own stack templates to reproduce a multi-resource setup on demand. A template starts private to your organization, and you can later share it with colleagues or publish it to the platform marketplace.
Template structure
A template has two parts: a metadata envelope and a descriptor that declares the resources the stack composes.
curl -X POST https://api.foundrydb.com/stacks/templates \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-cms",
"display_name": "My CMS",
"description": "A PostgreSQL-backed Directus installation.",
"version": "1.0.0",
"visibility": "private",
"descriptor": {
"apiVersion": "stacks.foundrydb.com/v1",
"resources": [
{
"name": "db",
"kind": "database",
"spec": {
"database_type": "postgresql",
"version": "17",
"plan_name": "tier-2",
"storage_size_gb": 50,
"storage_tier": "maxiops"
}
},
{
"name": "app",
"kind": "app",
"spec": {
"attachment_kind": "directus",
"attached_to": "$db"
}
}
],
"dependencies": {
"app": ["db"]
}
}
}'
The template starts in draft publication status and is only visible to your organization.
Descriptor fields
| Field | Required | Description |
|---|---|---|
apiVersion | Yes | Must be stacks.foundrydb.com/v1. |
resources | Yes | List of resource definitions. At least one required. |
dependencies | No | Maps a resource symbolic name to the list of resource names that must reach Running before it is created. Defines provisioning order. |
costPreview.inferenceBudgetUsdMonthly | No | Monthly spend ceiling for any inference key the stack mints. Shown as an is_ceiling line item in cost previews. |
Resource kinds
Each resource in resources has a name (symbolic, used in $ref tokens and dependencies), a kind, and a spec.
database
Provisions a managed database service.
| Spec key | Required | Description |
|---|---|---|
database_type | Yes | postgresql, mysql, mongodb, valkey, kafka, or opensearch. |
version | Yes | Engine version string (e.g. "17" for PostgreSQL). |
plan_name | Yes | Compute plan (e.g. "tier-2"). |
storage_size_gb | No | Data disk size in GB. 1–1024. |
storage_tier | No | "standard" or "maxiops". |
extensions | No | List of database extensions to enable (e.g. ["pgvector", "postgis"]). |
files
Provisions an object-storage bucket.
| Spec key | Required | Description |
|---|---|---|
quota_soft_gb | No | Soft quota in GB. 1–1024. |
quota_hard_gb | No | Hard quota in GB. 1–1024. |
inference
Mints an EU-routed inference key against your organization's configured provider.
| Spec key | Required | Description |
|---|---|---|
eu_only | No | Boolean. When true, requests are restricted to EU inference endpoints. |
monthly_budget_usd | No | Per-month spend ceiling in USD. Shown as is_ceiling: true in cost previews. |
app
Deploys a hosted application. The platform wires database credentials and bucket credentials into the app's environment automatically via attached_to references.
Trusted (private and org-shared) templates:
| Spec key | Required | Description |
|---|---|---|
attachment_kind | No | First-party companion app identifier (e.g. "open-webui", "directus", "metabase", "nocodb", "hasura"). Mutually exclusive with image_ref. |
image_ref | No | Container image reference for a custom app. Not allowed in public templates. |
container_port | No | Port the container listens on. Not allowed in public templates. |
plan_name | No | Compute plan. |
storage_size_gb | No | Disk size in GB. 1–1024. |
storage_tier | No | "standard" or "maxiops". |
attached_to | No | $ref pointing to a database or files resource whose credentials should be injected. |
env | No | Custom environment variables. Subject to env-shadow protection (see below). |
auth | No | Basic auth config for the app's endpoint. |
domain | No | Custom domain for the app. |
Public templates have a narrower allow-list. The image_ref, container_port, and env keys are not available. A public template's app must use a first-party companion app via attachment_kind.
Cross-resource references
Use $ref tokens in spec values to wire one resource's output into another's configuration. The reconciler resolves them after the upstream resource reaches Running.
{
"name": "app",
"kind": "app",
"spec": {
"attachment_kind": "open-webui",
"attached_to": "$db"
}
}
The platform injects the appropriate connection variables (see Env-shadow protection) based on the upstream resource kind.
Env-shadow protection
When your app resource includes an env map, the following keys are reserved by the platform and may not be set by a template author:
DATABASE_URL— injected whenattached_topoints to a database.S3_ENDPOINT,S3_BUCKET,S3_ACCESS_KEY,S3_SECRET— injected whenattached_topoints to a files resource.- Any key starting with
MDB_— reserved for platform-injected connection metadata.
Attempting to set any of these keys in env will fail validation with a descriptive error. The restriction applies at template save, at publish, and again at every launch as a defense in depth.
OPENAI_* keys are not restricted and are intentionally available for wiring the minted inference key into your application.
Storage sizing bounds
Any storage_size_gb, quota_soft_gb, or quota_hard_gb value must be between 1 and 1024 GB. Values outside this range are rejected at save time.
Dependency ordering
dependencies is a map from resource symbolic name to a list of resource names that must reach Running before it is created. Resources with no entries in dependencies are created in parallel in topological order.
{
"dependencies": {
"app": ["db", "files"],
"inference": []
}
}
In this example, db, files, and inference are created in parallel. app is created only after both db and files are Running.
Editing a template
A template in draft, rejected, or unpublished status can be updated:
curl -X PATCH https://api.foundrydb.com/stacks/templates/$TEMPLATE_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description.",
"version": "1.1.0",
"descriptor": { ... }
}'
On PATCH, only supplied fields are updated. A published or in-review template is immutable: create a new template (with a new name and version) to replace it.
Deleting a template
curl -X DELETE https://api.foundrydb.com/stacks/templates/$TEMPLATE_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"
Returns 204 No Content. Stacks already launched from this template keep running on their own snapshotted descriptor. Deletion prevents new launches from this template only.
Validation rules by visibility
| Rule | private | org_shared | public |
|---|---|---|---|
image_ref allowed on app | Yes | Yes | No |
container_port allowed on app | Yes | Yes | No |
env allowed on app | Yes | Yes | No |
App must use attachment_kind | No | No | Yes |
| Storage size max 1024 GB | Yes | Yes | Yes |
MDB_*, DATABASE_URL, S3_* env keys blocked | Yes | Yes | Yes |
Validation runs at save, at publish, and at every launch. A template that pre-dates a tightened rule and has not been re-saved will fail at launch if it no longer passes the current rules.
Next steps
Once your template is ready, see Publishing and the Marketplace to share it with your organization or the wider platform.