Reference
The one file that says what your application is: its containers and how each is built and tested, how it is deployed, and how its work is verified. Two shapes are covered here, an application and a Knative service. Specification v1.0.
You rarely write this file from scratch: a template comes with one, and the agent edits it when you say "add a second container" or "expose port 8080 at /api". Read this page when you want to know what a line means. A few dozen lines describe a whole application; the manifests under k8s/ are generated from them.
Thinkube GitOps explains what the file is for and what the platform generates from it. This page is the field-by-field lookup.
thinkube.yaml sits at the root of a repository. Thinkube Control reads it to build and deploy an application. Thinkube Tandem reads it to know where a repository’s tests live, how one of them runs, how the repository is made live when the platform does not do it, and where its documentation lands. Both read the file in place.
The example below is the file that deploys this documentation site:
# Copyright Alejandro Martínez Corriá and the Thinkube contributors
# SPDX-License-Identifier: Apache-2.0
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
metadata:
# The application's version; the generated app starts at it.
version: "0.1.0"
spec:
deployment:
type: app
containers:
- name: docs
build: .
port: 8080
size: small
health: /
routes:
- path: /
to: docs
The machine-readable schema is thinkube-yaml-v1.0.schema.json.
Principles
-
A static descriptor. It has no conditionals and two substitutions only:
{{ project_name }}and{{ domain_name }}. -
Open standards. What you build is standard: a git repository with ordinary code, a Containerfile and OCI container images. Your work is portable and not tied to Thinkube. The platform services it uses (a PostgreSQL database, S3-compatible storage, OpenID Connect sign-in, an OCI image registry) have close, highly compatible equivalents at the main cloud providers.
-
Written without Kubernetes knowledge. You name containers, ports, routes and services. The platform derives the rest.
-
One to many containers. Always-on applications, scale-to-zero Knative services, and platform components with a fixed name.
-
Declared, never guessed. What only you can know about a repository is written here. Facts the platform can find out by running a command are not written here.
Schema
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
metadata:
name: string # Application name
spec:
deployment: # Deployment type and scaling (optional)
type: string # "app" (default), "knative", "component", or "none"
name: string # Component only: fixed deployment name
replicas: number # Initial replica count (default 1; 0 for gateway-managed components)
gateway_managed: boolean # An external controller creates the pods (default false)
minScale: number # Knative: minimum pods (default 0)
maxScale: number # Knative: maximum pods (default 5)
containerConcurrency: number # Knative: concurrent requests per pod (default 0 = unlimited)
timeoutSeconds: number # Knative: request timeout in seconds (default 300)
containers: # The containers the platform builds and runs
- name: string # Container identifier
build: string # Build context path
port: number # Container port (optional)
size: string # small / medium / large / xlarge (optional)
health: string # Health endpoint path (required when a port is exposed)
schedule: string # Cron expression for scheduled tasks (optional)
mounts: # Storage mounts (optional)
- string # "storage-name:/mount/path"
gpu: # GPU requirements (optional)
count: number
memory: string
capabilities: # Special capabilities (optional)
- string # e.g. "large-uploads"
publicEnv: # Variables this container may show the browser (optional)
- string # A variable name, e.g. "APP_TITLE"
test: # Tests, run before the build in CI (optional)
enabled: boolean # default false
command: string # The whole suite (required when enabled)
image: string # Test image override (optional)
one: string # How one test file runs; <file> is substituted (optional)
migrations: # Database migrations (optional)
tool: string # alembic, django, flyway, liquibase, prisma, sequelize, gorm, custom
auto: boolean # Run on startup (default true)
parts: # Only for a repository with no containers: its trees, one toolchain each
- name: string
root: string # Path relative to the repository root
test: # As containers[].test
enabled: boolean
command: string
image: string
one: string
deploy: # How this repository is made live when the platform does not do it
run: string | list # The command, or several in order (optional)
in: string # The directory it runs in, when it lives in another repository (optional)
at: string # Where the result can be seen (optional)
verify: # For declarative work: how its own tool says the work holds
still: string | list # Commands that change nothing
apply: string # The command that does the work
ask: string # What reports whether anything is left to do
settled: string # What that report must say, e.g. "changed=0"
docs: # Where documentation lands (optional)
root: string # Default "docs"
routes: # HTTP routing (optional)
- path: string
to: string # A container name
services: # Platform services (optional)
- string # "database", "cache", "storage", "queue", "workflows", or "type:name"
dependencies: # Other deployed services this one needs (optional)
- name: string
type: string # A template type or service class
env: string # The variable that receives the resolved URL
env: # Variables you can set at deploy time (optional)
- name: string
description: string
default: string
metadata
| Field | Type | Description |
|---|---|---|
|
string |
The application name. Supports |
|
string |
The application’s version, |
The template an application was generated from has its own version: the newest vMAJOR.MINOR.PATCH tag of the template repository. A deploy uses that tag, and Copier records it in the application’s .copier-answers.yml.
spec.deployment
Optional. Defaults to type: app.
| Field | Type | Description |
|---|---|---|
|
string |
|
|
string |
Component only, and required there. Lowercase alphanumeric with hyphens. It becomes the namespace, the service name, the domain prefix, the Thinkube Git repository name and the ArgoCD application name. |
|
number |
Initial replica count, default |
|
boolean |
Default |
|
number |
Knative only. Default |
|
number |
Knative only. Default |
|
number |
Knative only. Default |
|
number |
Knative only. Default |
Knative portability
Knative services are meant to be portable to cloud serverless platforms. When type: knative, the schema enforces: a single container; no gpu, mounts, migrations, schedule or capabilities; no storage or workflows service; timeoutSeconds at most 900. Work that needs any of these uses app or component.
spec.containers
At least one container, unless deployment.type is none.
| Field | Type | Description |
|---|---|---|
|
string |
Unique identifier, used in routes. For example |
|
string |
Build context relative to the repository root: |
|
number |
The port the container listens on, 1 to 65535. Optional for workers and jobs. |
|
string |
|
|
string |
Health endpoint path, required when a port is exposed. Standard value |
|
string |
A cron expression. The container runs as a job and has no persistent pods. |
|
list |
|
|
object |
|
|
list |
|
|
list |
Names of variables this container may show the browser. See below. |
|
object |
See below. |
|
object |
|
containers[].test
Tests run in CI before the container is built, in the named image. The build and the deployment run only when the tests pass. Results are reported to Thinkube Control.
| Field | Type | Description |
|---|---|---|
|
boolean |
Default |
|
string |
Runs the whole suite. Required when enabled. |
|
string |
The image the tests run in. Optional; the platform chooses a base image for the container’s language. |
|
string |
Runs a single test file. |
Keep the test environment in one place. The application templates give each container a run_tests.sh that runs the whole suite without arguments and one file with a file argument, so command is ./run_tests.sh and one is ./run_tests.sh <file>, and the settings a test needs are set in the script, never repeated in this file.
containers[].publicEnv
Names of variables this container may show the browser.
A container reads its variables from its environment. A browser application cannot: it is a bundle built before the deployment exists, and by the time it runs it is a file served by a web server. publicEnv lets a variable reach the page. The platform sets PUBLIC_ENV_VARS in the container to the names listed here, and the image’s entrypoint writes those variables, and only those, where the page can read them.
containers:
- name: frontend
build: ./frontend
port: 80
health: /health
publicEnv:
- APP_TITLE
- API_BASE_URL
Any variable the container has can be named: one the platform sets such as APP_TITLE, one declared in spec.env, one wired from dependencies, or a parameter answered at deploy time. The list holds names, never values. Values reach the container from the deployment, so a template carries the list and never the answers.
The list is an allow-list, and an absent list means nothing is published. This matters: every container receives the whole environment, including POSTGRES_PASSWORD, KEYCLOAK_CLIENT_SECRET and ADMIN_PASSWORD. A container that wrote its environment to the page would serve those to every visitor. Name a variable here only when it is safe for anyone who opens the application to read it, and treat a secret in this list as a disclosed secret.
spec.parts
Only for a repository with deployment.type: none. Each part is a tree with its own toolchain: where its files are, and how its tests run there. A repository with containers does not declare parts; its containers are its parts.
| Field | Type | Description |
|---|---|---|
|
string |
The part’s identifier. |
|
string |
Path relative to the repository root. |
|
object |
As |
spec.deploy
How the repository is made live when the platform does not do it. An application usually omits this block: the platform deploys it from containers. It may still carry at on its own, to say where the result can be seen.
| Field | Type | Description |
|---|---|---|
|
string or list |
The command that makes it live, or several to run in order, stopping at the first that fails. Optional: no |
|
string |
The directory to run it in, when the command lives in another repository. Optional. |
|
string |
Where the result can be seen, a URL. Optional. Thinkube Tandem opens it to look at what was delivered. |
spec.verify
For repositories whose work is declarative, such as playbooks. For playbooks, the check is that a second run changes nothing.
| Field | Type | Description |
|---|---|---|
|
string or list |
Commands that change nothing: lint, syntax check, a dry run with a diff. One or several. Run before anything is merged. |
|
string |
The command that does the work. It is the deployment, and runs only after a person approves it. |
|
string |
The command that reports whether anything is left to do, run after |
|
string |
What that report must say for the work to be settled, for example |
spec.docs
Where documentation lands. Every delivery documents what it changed. When the work changes no documentation page, Thinkube Tandem adds one to what the change must deliver.
| Field | Type | Description |
|---|---|---|
|
string |
Default |
spec.routes
HTTP routes, implemented as Gateway API HTTPRoute resources with the platform’s wildcard certificates.
| Field | Type | Description |
|---|---|---|
|
string |
A path prefix: |
|
string |
The container that serves it. |
spec.services
Platform services the application needs. Each declared service injects variables into every container.
| Service | Variables injected into every container |
|---|---|
|
|
|
|
|
|
|
|
|
|
database, cache, storage and queue take an optional type:name suffix for a named instance, such as storage:uploads. workflows does not: an application has one.
workflows is refused for deployment.type: knative. See Knative portability.
Workflows
workflows gives the application its own access to Argo Workflows, in its own namespace. The platform creates a ServiceAccount <app>-workflows that every pod runs as, a Role allowing it to manage workflows in that namespace, and a default artifact repository in Thinkube Storage under the prefix <app>/. Steps run in the application’s own images, named by CONTAINER_IMAGE_<NAME>, and count against the namespace’s ResourceQuota. See Run a pipeline from your app.
spec.dependencies
Other deployed services this application needs. At deploy time Thinkube Control resolves each to the running service’s cluster URL and injects it as the named variable. If the dependency is not deployed, the deployment fails and says which one.
| Field | Type | Description |
|---|---|---|
|
string |
The name shown in the interface and in errors. |
|
string |
The template type or service class to look for: |
|
string |
The variable that receives the URL: |
spec.env
Variables you can set when you deploy. Thinkube Control shows them with their descriptions; a variable with no default is required. They are injected into every container. A variable wired from dependencies wins over an entry here with the same name.
| Field | Type | Description |
|---|---|---|
|
string |
Uppercase with underscores. |
|
string |
Shown in the deployment interface. |
|
string |
Optional. |
A variable declared here reaches the containers. To let a browser application read one, name it in that container’s publicEnv.
Examples
A minimal application
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
metadata:
name: "{{ project_name }}"
spec:
containers:
- name: app
build: .
port: 3000
health: /health
A web application with a database and tests
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
metadata:
name: "{{ project_name }}"
spec:
containers:
- name: backend
build: ./backend
port: 8000
health: /health
test:
enabled: true
command: "pytest --cov=app"
one: "pytest <file>"
migrations:
tool: alembic
auto: true
- name: frontend
build: ./frontend
port: 80
health: /health
publicEnv:
- APP_TITLE
test:
enabled: true
command: "npx vitest run"
one: "npx vitest run <file>"
routes:
- path: /api
to: backend
- path: /
to: frontend
services:
- database
A Knative processing service
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
metadata:
name: "{{ project_name }}"
spec:
deployment:
type: knative
minScale: 0
maxScale: 3
containerConcurrency: 1
timeoutSeconds: 600
containers:
- name: aligner
build: .
port: 8080
size: medium
health: /health
dependencies:
- name: texplitter
type: texplitter
env: TEXPLITTER_URL
- name: ollama
type: ollama
env: OLLAMA_URL
env:
- name: OLLAMA_MODEL
description: "LLM model for alignment validation"
default: "qwen3.5-ner:notk"
routes:
- path: /
to: aligner
A web application with a pipeline
The verification application of Run a pipeline from your app: the web app template with workflows added to its services.
apiVersion: thinkube.io/v1
kind: ThinkubeDeployment
spec:
deployment:
type: app
containers:
- name: backend
build: ./backend
port: 8000
size: medium
health: /health
test:
enabled: true
image: "python-base:3.12-slim"
command: "./run_tests.sh"
one: "./run_tests.sh <file>"
migrations:
tool: alembic
auto: true
- name: frontend
build: ./frontend
port: 80
size: small
health: /health
publicEnv:
- APP_TITLE
test:
enabled: true
# node-base carries git, which npm needs to install thinkube-style
image: "node-base:22-alpine"
command: "./run_tests.sh"
one: "./run_tests.sh <file>"
routes:
- path: /api
to: backend
- path: /
to: frontend
services:
- database
- workflows