Working with Recipes
Recipes are custom build steps based on one or multiple standard TeamCity steps. If TeamCity's built-in steps lack a needed option, and you frequently emulate it (for example, use a CLI step to upload artifacts via a cloud provider API), you can save this custom step as a reusable recipe.
Creating a recipe is a simpler alternative to developing a TeamCity plugin that implements a custom build step.
note
TeamCity Recipes are still under active development. In the future releases, we expect to release the following features:
Support for local recipes in YAML format
Ability to share your custom recipes on JetBrains Marketplace
We value your feedback and encourage you to share ideas and public recipe suggestions.
What are recipes?
Recipes are custom build steps made from default TeamCity build steps pre-set in the specific manner. Complex recipes can include other recipes as building blocks.
What's the point of a recipe?
Recipes allow you to wrap pre-customized TeamCity build steps into a new step that can be easily shared across build configurations.
What's the difference between recipes and meta-runners?
In version 2025.03, "meta-runners" were renamed to "recipes". While based on the same concept, recipes offer added benefits like YAML support and easy sharing on JetBrains Marketplace.
Can I continue use my existing meta-runners and TeamCity Meta-Runner Pack?
Yes, meta-runners are still functional under the new name and require no manual updates.
What are public recipes?
Public recipes are those shared at JetBrains Marketplace. Currently, only recipes hand-crafted by JetBrains are available. Future releases will support community-shared recipes from TeamCity users.
Are public recipes safe?
You can manually download a recipe .yml definition file from JetBrains Marketplace to review it before adding this recipe to your configuration. Future releases will offer a more intuitive way to inspect recipe code.

I want to create a recipe, what should I do?
Find an existing or create a new build configuration that performs an action you want to save as a custom build step, and extract a recipe using the configuration Actions menu in TeamCity UI.
How to use a recipe?
In the same way you utilize regular build steps: add them to the configuration's "Build steps" list.
Are recipes editable?
Yes, you do not need to re-configure a source configuration and re-extract a recipe every time you need to make a change. Local recipes can be edited on the Recipes page of project settings.
The most straightforward way to create a new recipe is to extract it from an existing configuration that uses a required step or sequence of steps. For example, the Kotlin DSL example below shows a build configuration with two CLI build steps: one uses cURL to download a file, and the other runs ls
to list the working directory contents.
import jetbrains.buildServer.configs.kotlin.*
object SourceConfiguration : BuildType({
name = "Source Configuration"
params {
param("URL", "")
param("fileName", "")
}
steps {
script {
id = "simpleRunner"
scriptContent = "curl -o %URL% %fileName%"
}
script {
id = "simpleRunner_1"
scriptContent = "ls"
}
}
})
To extract a recipe from an existing configuration:
In configuration settings, invoke the Actions menu and click Extract recipe.
In the popup dialog, enter the recipe's internal ID, public name, and description. Recipes show these strings on the Add build step page.
Click Extract to create your new recipe. You recipe should look like the following:
<meta-runner name="cURL: File Download"> <description>A two-step recipe that utilizes the "curl -o %URL% %fileName%" command to download a file, and calls "ls" command to print the contents of a working directory afterwards</description> <settings> <parameters> <param name="URL" value="" spec="text description='The URL of a file to be downloaded' display='normal' label='Download URL:'"/> <param name="fileName" value="" spec="text description='Enter the saved file name or leave blank to keep the origin name' label='File name:'" /> <!--other parameters--> </parameters> <build-runners> <runner name="" type="simpleRunner"> <parameters> <param name="script.content" value="curl -o %URL% %fileName%" /> <param name="teamcity.step.mode" value="default" /> <param name="use.custom.script" value="true" /> </parameters> </runner> <runner name="" type="simpleRunner"> <parameters> <param name="script.content" value="ls" /> <param name="teamcity.step.mode" value="default" /> <param name="use.custom.script" value="true" /> </parameters> </runner> </build-runners> <requirements /> </settings> </meta-runner>
note
By default, a recipe includes all parameters from the source configuration. You can remove unnecessary ones and edit parameter
spec
attributes to adjust appearance and behavior on the Recipes page: Edit a Local Recipe.
Recipes are saved to the <TeamCity Data Directory>
\config\projects\<project_ID>\pluginData\metaRunners
directory. They are owned by a project whose configuration was used as a source. As such, recipes are by default available only for their origin project and its subprojects.
tip
To make a recipe available for the entire TeamCity server, move its configuration file to the
<TeamCity Data Directory>
\config\projects\_Root\pluginData\metaRunners
directory.
Recipes are custom build steps, and as such, are added to build configurations in the same manner.
Open configuration settings and navigate to the Build steps settings tab.
Click the Add build step button.
Choose a recipe from the right column that shows:
local recipes owned by this project or its parent project;
public recipes from JetBrains Marketplace.
Set up required recipe settings in the same manner you do this for regular TeamCity steps.
You can explore public recipes by the TeamCity team at https://plugins.jetbrains.com/teamcity_recipe. We hope to expand our collection in future release cycles and welcome your ideas and feedback.
If you do not see any Marketplace recipe options, verify they are enabled for your project:
Open project settings and navigate to the Recipes settings tab.
Switch the Public JetBrains Marketplace recipes setting to "Enabled". If this setting is "Disabled" and grayed-out, edit the settings of a parent project that enforces this behavior or talk to a person who administers this project.
If you have a recipe .xml definition file, you can upload this file to a required project manually. For example, you may want to move a recipe from one project to another or downloaded a recipe manually from JetBrains Marketplace.
To install a recipe from a file, do the following:
Open project settings and navigate to the Recipes settings tab.
Click the Upload Recipe button.
Choose a configuration file and enter a unique recipe name.
Click Save. Your uploaded recipe is now available for all configuration of this project and its subprojects.
Recipes extracted from a build configuration or uploaded from a file are stored locally on your server machine and can be edited in TeamCity UI.
Open project settings and navigate to the Recipes settings tab.
Click a recipe from the Local Recipes table to view and edit its configuration file.
For example, a recipe extracted from an existing configuration copies all parameters from this configuration. You can remove parameters unrelated to actual build steps performed by a recipe.
Parameters defined in a recipe configuration file can have additional attributes that specify their appearance and behavior. To add a parameter specification, add the spec="type attribute='value'
format:
<parameters>
<param name="internalName" value="" spec="type attribute1='value1' attribute2='value2'/>
</parameters>
The specification supports the following attributes:
- label
A public parameter name visible in TeamCity UI. If not set, the parameter
name
is shown instead.- description
A public description displayed in TeamCity UI below the parameter's editor.
- type
Specifies the editor type:
text
— a regular textbox-based parameter. This is the default behavior.password
— a password parameter that uses password chars to mask its actual values.checkbox
— a two-state parameter that displays a checkbox in the TeamCity UI. Requires thecheckedValue='value1' uncheckedValue='value2'
attributes to specify actual parameter values for checked and unchecked states.select
— a drop-down menu that displays options specified in thedata_N='value'
format.
- display
Specifies the display options of an editor. Supported values:
normal
— a regular display mode. This is the default behavior.prompt
— forces the Run custom build dialog to pop up every time a build starts. The dialog highlights all "prompt" parameters to make sure users start a build with the required value.hidden
— prevents users from changing this parameter's value.
- validationMode
Allows you to validate parameter values. Supported values:
any
— a parameter can have any value. This is the default behavior.not_empty
— a parameter cannot be blank.regex
— validates the parameter value using a regular expression specified in theregexp='...'
attribute.
Examples:
# Checkbox parameter
<param name="enabled" value="" spec="checkbox checkedValue='true' uncheckedValue='false' label='Enable debug' description='Tick this setting to run in debug mode'"/>
# Select parameter
<param name="logBehavior" value="" spec="select data_1='All' data_2='Errors only' data_3='Errors and warnings' label='Logging verbosity:' description='Choose whether only critical or all messages should be logged'"/>
# Prompt parameter that cannot have an empty value
<param name="tag" value="default" spec="text description='This value cannot be empty' label='Tag: ' validationMode='not_empty' display='prompt'" />
Recipes are designed to be reused throughout build configurations and as such, should be configuration-agnostic. This means your recipes should ideally perform actions that can be executed regardless of other configuration settings.
In addition, recipes have the same project requirements as their origin build steps. You may opt for cross-platform build steps (like Command Line or Kotlin Script) as a basis for your recipes to make them compatible with as many build agents as possible.
VCS Roots are not baked into a recipe configuration file. As such, if you create a recipe that performs operations on repository files and folders, configurations without suitable VCS Roots will fail. If you need such recipe, do the following:
Go the settings of a build configuration that imports a recipe.
Switch to the Version Control Settings tab.
Click the Attach VCS root button.
In the Attach existing VCS root choose the same root your origin configuration uses.
Click Save at the bottom of the page and run your build configuration. Since it now has a connection to a VCS repository, build steps can access required files and are able to finish successfully.
Gradle, Maven, Ant, and other build steps process build files like build.xml
or pom.xml
. If your custom recipe includes such a step, ensure the importing build configuration can locate the required file to avoid failures.
Build steps that allow defining a build file directly rather than just specifying a path are particularly well-suited for use in recipes. For example, the Ant build step.

Individual build steps comprise recipes have settings that allow TeamCity to run these steps inside Docker/Podman containers. Same settings are available for recipes themselves.

If you want all of your steps to be executed inside a required container, set up the required image on the recipe level. Container settings of individual steps have a priority over these recipe settings and allow you to run each step in its unique container.
Kotlin sample of a recipe that runs its steps inside "ubuntu" Linux container:
object Build : BuildType({
steps {
step {
id = "SimpleMetaRunner"
type = "idSimpleMetaRunner"
executionMode = BuildStep.ExecutionMode.DEFAULT
param("plugin.docker.imageId", "ubuntu")
param("plugin.docker.imagePlatform", "linux")
param("plugin.docker.pull.enabled", "true")
param("plugin.docker.run.parameters", "")
}
}
})
The XML markup for this recipe is shown below. Step #1 runs in the python:3.9.20-bullseye
container. Step #2 has no personal container settings and runs inside the ubuntu
container as defined in the Kotlin code above.
<meta-runner name="SimpleMetaRunner">
<description>A Py/CLI sample recipe</description>
<settings>
<parameters/>
<build-runners>
<runner name="Py" type="python-runner">
<parameters>
<param name="plugin.docker.imageId" value="python:3.9.20-bullseye" />
<!-- Python step parameters -->
</parameters>
</runner>
<runner name="" type="simpleRunner">
<parameters>
<!-- CLI step parameters -->
</parameters>
</runner>
</build-runners>
<requirements />
</settings>
</meta-runner>
Thanks for your feedback!