{"id":406,"date":"2026-10-14T03:31:05","date_gmt":"2026-10-14T03:31:05","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2015\/02\/24\/going-meta-with-meta-runners-in-teamcity\/"},"modified":"2026-10-14T03:31:05","modified_gmt":"2026-10-14T03:31:05","slug":"going-meta-with-meta-runners-in-teamcity","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/going-meta-with-meta-runners-in-teamcity\/","title":{"rendered":"TeamCity Meta-Runners: Reuse Build Steps with Parameterized Runner Types"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Originally published on 2015-02-24; fully revised and updated.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">TeamCity Meta-Runners let you convert repeated build logic into reusable runner types with configurable parameters. This is especially useful for teams managing many similar build configurations and wanting less duplication, fewer copy-paste errors, and more consistent CI behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Official reference:<\/strong> <a href=\"https:\/\/confluence.jetbrains.com\/display\/TCD9\/Working+with+Meta-Runner\">https:\/\/confluence.jetbrains.com\/display\/TCD9\/Working+with+Meta-Runner<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2015\/02\/teamcity-meta-with-meta-run-1.png\" alt=\"TeamCity Meta-Runners\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">What Meta-Runners solve in TeamCity<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A standard build runner can execute a task in one build configuration. A Meta-Runner goes further: it packages one or more build steps and exposes parameters so the same logic can be reused in many configurations. This is ideal when multiple pipelines repeat the same deployment, packaging, or environment setup sequence.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Reduce duplicated step definitions across build configurations.<\/li><li>Expose configurable inputs (for example remote URLs) as parameters.<\/li><li>Keep behavior consistent while allowing per-configuration values.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Meta-Runners vs templates vs copied steps<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Approach<\/th><th>Best for<\/th><th>Reuse scope<\/th><th>Important limit<\/th><\/tr><\/thead><tbody><tr><td>Build template<\/td><td>Standardizing a full build configuration structure<\/td><td>Whole configuration<\/td><td>A build configuration can be based on only one template<\/td><\/tr><tr><td>Meta-Runner<\/td><td>Reusing repeated build step logic with parameters<\/td><td>One or more steps across many configurations<\/td><td>Needs good parameter design and maintenance<\/td><\/tr><tr><td>Copied command-line step<\/td><td>Quick one-off setup<\/td><td>Single configuration copy<\/td><td>High drift risk as copies diverge over time<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Workflow: extract once, reuse across configurations<\/h2>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Identify repeated build steps] --> B[Replace hardcoded values with TeamCity parameters]\n    B --> C[Validate the original build configuration]\n    C --> D[Use Extract Meta-Runner in settings]\n    D --> E[Review generated steps and parameters]\n    E --> F[Save Meta-Runner at project level]\n    F --> G[Add new build step using Meta-Runner type]\n    G --> H[Set parameter values per configuration]\n    H --> I[Run build and verify output]\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Parameterize the build step before extraction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original example deploys to Heroku with a command-line runner. To make it reusable, replace hardcoded values with a build parameter such as <strong>%heroku.remote%<\/strong>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css language-bash\"><span class=\"hljs-selector-tag\">git<\/span> <span class=\"hljs-selector-tag\">remote<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-tag\">heroku<\/span> %<span class=\"hljs-selector-tag\">heroku<\/span><span class=\"hljs-selector-class\">.remote<\/span>%\n<span class=\"hljs-selector-tag\">git<\/span> <span class=\"hljs-selector-tag\">push<\/span> <span class=\"hljs-selector-tag\">heroku<\/span> <span class=\"hljs-selector-tag\">master<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Define <strong>heroku.remote<\/strong> in the build configuration parameters so each configuration can provide its own remote target.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Parameter<\/th><th>Example value<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td>heroku.remote<\/td><td>git@heroku.com:django-ci-example.git<\/td><td>Selects the git remote used by the deploy step<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Extract the Meta-Runner<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Open the build configuration settings that contains the reusable steps.<\/li><li>Use the <strong>Extract Meta-Runner<\/strong> action from the configuration UI.<\/li><li>Provide a clear name (this appears later in the <strong>Runner type<\/strong> list).<\/li><li>Save and review the generated Meta-Runner under project-level <strong>Meta-Runners<\/strong>.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Meta-Runners are represented as TeamCity XML configuration and can be edited from the web interface to keep only required steps and parameters.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Use the Meta-Runner in other build configurations<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Create or edit a build step in another configuration.<\/li><li>Select the extracted Meta-Runner in <strong>Runner type<\/strong>.<\/li><li>Set parameter values for that configuration (for example a different remote).<\/li><li>Save and run the build to confirm behavior matches the original step set.<\/li><\/ul>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-bash\"><span class=\"hljs-comment\"># Example per-configuration parameter value<\/span>\nheroku.remote=git@heroku.com:my-team-app.git<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Operational notes for Heroku-style git deploys<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original example uses git remote push over SSH from a TeamCity agent. This requires agent-side SSH key setup and is shown as a Meta-Runner illustration, not necessarily as a production deployment pattern. The original source also mentions <a href=\"https:\/\/github.com\/moredip\/heroku-headless\">heroku-headless<\/a> as an alternative approach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls and best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Do not hardcode environment-specific values.<\/strong> Always expose them as parameters.<\/li><li><strong>Keep Meta-Runners focused.<\/strong> Prefer small, reusable step groups over very large all-in-one runners.<\/li><li><strong>Name parameters clearly.<\/strong> Use predictable keys so teams understand required inputs quickly.<\/li><li><strong>Review defaults carefully.<\/strong> Remove unsafe defaults in Meta-Runner XML when accidental reuse could cause wrong deployments.<\/li><li><strong>Version and document usage.<\/strong> Track where each Meta-Runner is used to avoid breaking downstream configurations during edits.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Meta-Runners in TeamCity help you package repeated build steps into reusable, parameterized runner types. This guide shows when to use them, how to extract one, and how to apply it safely across build configurations.<\/p>\n","protected":false},"author":1,"featured_media":3508,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[449],"tags":[1753,101,1751,1752,1754,144,1755],"class_list":["post-406","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-teamcity-build-management-tools","tag-build-configuration","tag-continuous-integration","tag-meta","tag-meta-runners","tag-meta-runners-in-teamcity","tag-teamcity","tag-working-with-meta-runner"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/406","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=406"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/406\/revisions"}],"predecessor-version":[{"id":79273,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/406\/revisions\/79273"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/3508"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=406"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=406"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=406"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}