TeamCity: Complete Configuration Guide
TeamCity is a powerful, Java-based continuous integration and continuous deployment (CI/CD) server developed by JetBrains. It helps development teams automate building, testing, and deploying applications across various platforms and languages. Whether you are running a small startup or managing enterprise-scale infrastructure, TeamCity provides a flexible, plugin-rich environment that integrates seamlessly with popular version control systems, build tools, and cloud providers.
Why TeamCity Matters
In modern software development, manual builds and deployments are error-prone and slow. TeamCity addresses this by providing a centralized platform where every code change triggers an automated pipeline. This ensures that bugs are caught early, releases are predictable, and teams can ship software with confidence. Key benefits include:
- Early bug detection: Automated tests run on every commit, catching regressions before they reach production.
- Scalability: Build agents can be distributed across multiple machines, enabling parallel execution of large pipelines.
- Rich ecosystem: Hundreds of plugins extend functionality for languages, frameworks, and deployment targets.
- Detailed reporting: Real-time build logs, test history, and code coverage dashboards provide deep visibility.
- Flexible configuration: Pipelines can be defined via the UI, Kotlin DSL, or stored as code in version control.
Architecture Overview
TeamCity operates on a client-server architecture. The TeamCity Server is the central component that manages configuration, scheduling, and reporting. Build Agents are separate processes that execute the actual build and test jobs. A single server can coordinate dozens or even hundreds of agents, and agents can run on physical machines, virtual machines, or containers.
Installing TeamCity
TeamCity can be installed on Windows, Linux, or macOS. The simplest way to get started is using Docker, which bundles the server and an internal database. For production, you should connect an external database such as PostgreSQL or MySQL.
Running TeamCity with Docker
The following Docker Compose file launches a TeamCity server along with a PostgreSQL database and one build agent:
version: "3.8"
services:
teamcity-server:
image: jetbrains/teamcity-server:latest
ports:
- "8111:8111"
volumes:
- teamcity-data:/data/teamcity_server/datadir
- teamcity-logs:/opt/teamcity/logs
depends_on:
- postgres
postgres:
image: postgres:15
environment:
POSTGRES_DB: teamcity
POSTGRES_USER: teamcity
POSTGRES_PASSWORD: secure_password
volumes:
- pg-data:/var/lib/postgresql/data
teamcity-agent:
image: jetbrains/teamcity-agent:latest
environment:
- SERVER_URL=http://teamcity-server:8111
- AGENT_NAME=DefaultAgent
depends_on:
- teamcity-server
volumes:
teamcity-data:
teamcity-logs:
pg-data:
Start the stack with the following command:
docker-compose up -d
Once running, navigate to http://localhost:8111 in your browser to complete the initial setup wizard. You will be prompted to choose a database type, create an administrator account, and accept the license agreement.
Core Concepts
Before diving into configuration, it is important to understand the core building blocks of TeamCity:
- Project: A top-level container that groups related configurations, such as a single application or microservice.
- Build Configuration: A defined sequence of steps that produces a build artifact, runs tests, or performs deployment.
- Build Step: An individual action within a build configuration, such as running a Maven goal, executing a shell script, or invoking a Docker command.
- VCS Root: A connection to a version control repository that supplies source code to build configurations.
- Build Trigger: A rule that automatically starts a build, such as on every commit, on a schedule, or after another build finishes.
- Build Agent: A worker process that executes build steps. Agents can be authorized, unauthorized, or disabled.
- Build Queue: A queue where pending builds wait until a compatible agent becomes available.
Configuring Your First Project
After installation, the next step is to create a project and connect it to a source repository. TeamCity supports Git, Subversion, Mercurial, Perforce, and Team Foundation Version Control out of the box.
Creating a Project from a Repository URL
The fastest way to set up a project is to let TeamCity auto-detect build steps from your repository. Navigate to Administration → Projects and click Create project. Choose From a repository URL and enter your Git URL:
https://github.com/your-org/your-repo.git
TeamCity will scan the repository and suggest build configurations based on detected files such as pom.xml, package.json, build.gradle, or Dockerfile. Review the suggestions and click Create.
Manually Adding a VCS Root
If you prefer manual configuration, go to your project settings and add a VCS root. The following example shows a Git VCS root configuration in the Kotlin DSL format, which can be stored in version control:
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.vcs.GitVcsRoot
project {
vcsRoot(MainVcsRoot)
}
object MainVcsRoot : GitVcsRoot({
name = "Main Repository"
url = "https://github.com/your-org/your-repo.git"
branch = "refs/heads/main"
branchSpec = "+:refs/heads/*"
authMethod = password {
userName = "ci-user"
password = "credentialsJSON:github-token"
}
})
Defining Build Steps
Build steps are the heart of any build configuration. Each step runs a specific command or tool, and steps execute sequentially unless parallel execution is configured. TeamCity provides built-in runners for Maven, Gradle, MSBuild, npm, Docker, and more.
Example: Node.js Build Configuration
The following Kotlin DSL example defines a build configuration for a Node.js application. It installs dependencies, runs linting, executes unit tests, and builds the production bundle:
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.buildSteps.npm
object Build : BuildType({
name = "Build and Test"
vcs { root(MainVcsRoot) }
steps {
npm {
name = "Install Dependencies"
commands = "ci"
}
script {
name = "Lint"
scriptContent = "npm run lint"
}
script {
name = "Unit Tests"
scriptContent = "npm run test:unit"
}
npm {
name = "Build"
commands = "run build"
}
}
artifactRules = "dist/** => dist.zip"
})
Example: Docker Build and Push
For containerized applications, you can build an image and push it to a registry within the same pipeline:
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.dockerCommand
object DockerBuild : BuildType({
name = "Docker Build and Push"
vcs { root(MainVcsRoot) }
params {
param("env.IMAGE_NAME", "my-app")
param("env.REGISTRY", "registry.example.com")
param("env.BUILD_NUMBER", "%build.counter%")
}
steps {
dockerCommand {
name = "Build Image"
commandType = build {
source = file {
path = "Dockerfile"
}
namesAndTags = """
%env.REGISTRY%/%env.IMAGE_NAME%:%env.BUILD_NUMBER%
%env.REGISTRY%/%env.IMAGE_NAME%:latest
""".trimIndent()
}
}
dockerCommand {
name = "Push Image"
commandType = push {
namesAndTags = """
%env.REGISTRY%/%env.IMAGE_NAME%:%env.BUILD_NUMBER%
%env.REGISTRY%/%env.IMAGE_NAME%:latest
""".trimIndent()
}
}
}
})
Configuring Build Triggers
Triggers automate the execution of builds. The most common trigger is the VCS trigger, which starts a build whenever changes are detected in the connected repository.
VCS Trigger
import jetbrains.buildServer.configs.kotlin.triggers.vcs
object Build : BuildType({
name = "Build on Commit"
vcs { root(MainVcsRoot) }
triggers {
vcs {
branchFilter = "+:<default>"
quietPeriodMode = VCS_TRIGGER_QUIET_PERIOD
quietPeriod = 60
}
}
})
The quietPeriod setting introduces a 60-second delay after the last commit before triggering a build. This is useful when developers push multiple commits in quick succession, as it batches them into a single build.
Scheduled Trigger
Scheduled triggers are useful for nightly builds, periodic integration tests, or cleanup tasks:
import jetbrains.buildServer.configs.kotlin.triggers.schedule
object NightlyBuild : BuildType({
name = "Nightly Build"
vcs { root(MainVcsRoot) }
triggers {
schedule {
schedulingPolicy = daily {
hour = 2
minute = 0
}
branchFilter = "+:refs/heads/main"
triggerBuild = always()
}
}
})
Managing Build Dependencies and Snapshots
Complex pipelines often require one build to depend on the output of another. TeamCity supports two types of dependencies: artifact dependencies, where a build downloads artifacts produced by another build, and snapshot dependencies, which ensure that dependent builds use the same source code revision.
Artifact Dependency Example
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildFeatures.artifacts
object Deploy : BuildType({
name = "Deploy to Staging"
vcs { root(MainVcsRoot) }
dependencies {
artifacts(Build) {
artifactRules = "dist.zip => dist"
buildRule = lastSuccessful()
}
}
steps {
script {
name = "Deploy"
scriptContent = """
unzip dist/dist.zip -d /var/www/app
systemctl restart my-app
""".trimIndent()
}
}
})
Snapshot Dependency Example
object IntegrationTest : BuildType({
name = "Integration Tests"
vcs { root(MainVcsRoot) }
dependencies {
snapshot(Build) {
onDependencyFailure = FailureAction.FAIL_TO_START
}
}
steps {
script {
name = "Run Integration Tests"
scriptContent = "npm run test:integration"
}
}
})
Working with Build Agents
Build agents are the workhorses of TeamCity. Each agent has a set of capabilities, including installed tools, operating system, and environment variables. TeamCity matches build configurations to compatible agents based on required capabilities.
Installing a Build Agent
To install a build agent on a Linux machine, download the agent ZIP from the TeamCity server and extract it:
wget http://teamcity-server:8111/update/buildagent.zip
unzip buildagent.zip -d /opt/teamcity-agent
cd /opt/teamcity-agent/conf
cp buildAgent.dist.properties buildAgent.properties
Edit buildAgent.properties to point the agent to your server:
serverUrl=http://teamcity-server:8111
name=Linux-Agent-01
workDir=/opt/teamcity-agent/work
tempDir=/opt/teamcity-agent/temp
systemDir=/opt/teamcity-agent/system
Start the agent:
cd /opt/teamcity-agent/bin
./agent.sh start
The agent will appear in the TeamCity UI under Agents → Unauthorized. Authorize it to begin assigning builds.
Defining Agent Requirements
You can restrict a build configuration to run only on agents with specific capabilities. For example, to require Docker on the agent:
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildFeatures.requirements
object DockerBuild : BuildType({
name = "Docker Build"
vcs { root(MainVcsRoot) }
requirements {
contains("teamcity.agent.jvm.os.name", "Linux")
exists("docker.version")
}
steps {
dockerCommand {
name = "Build"
commandType = build {
source = file { path = "Dockerfile" }
}
}
}
})
Handling Credentials and Secrets
Storing secrets in plain text is a security risk. TeamCity provides a built-in credentials vault that encrypts sensitive values such as API tokens, passwords, and SSH keys. Encrypted values are referenced using the credentialsJSON prefix.
Using Encrypted Parameters
First, create a project-level parameter of type Password in the TeamCity UI. Then reference it in your configuration:
params {
param("env.NPM_TOKEN", "credentialsJSON:npm-publish-token")
param("env.DATABASE_URL", "credentialsJSON:database-url")
}
In a build step, the token is injected as an environment variable without being exposed in build logs:
steps {
script {
name = "Publish Package"
scriptContent = """
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc
npm publish
""".trimIndent()
}
}
Notifications and Reporting
TeamCity can notify teams about build results through email, Slack, Microsoft Teams, and other channels. Notifications can be configured at the user level or globally for the entire project.
Slack Notification via Build Script
For custom notifications, you can use a build step that posts to a Slack webhook:
steps {
script {
name = "Notify Slack"
scriptContent = """
curl -X POST -H 'Content-type: application/json' \\
--data "{\"text\":\"Build %system.teamcity.buildConfName% #%build.number% finished with status %system.build.status%\"}" \\
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
""".trimIndent()
executionMode = BuildStep.ExecutionMode.ALWAYS
}
}
Setting executionMode = BuildStep.ExecutionMode.ALWAYS ensures the notification runs even if previous steps fail.
Best Practices
- Store configuration as code: Use the Kotlin DSL to version-control your pipeline definitions. This enables code review, branching, and rollback of configuration changes.
- Keep builds fast: Aim for build times under ten minutes. Use parallel execution, incremental builds, and caching to reduce wait times.
- Use artifact dependencies wisely: Build once and pass artifacts downstream rather than rebuilding the same code in multiple configurations.
- Isolate agents: Run agents in containers or virtual machines to prevent environment drift and improve security.
- Secure secrets: Never hardcode passwords or tokens in build scripts. Always use TeamCity's encrypted parameter store.
- Monitor agent health: Regularly check agent disk space, memory usage, and build queue length to prevent bottlenecks.
- Enable clean checkout when needed: Use clean checkout rules for builds that are sensitive to leftover files, but avoid it on every build to save time.
- Use failure conditions: Configure build failure conditions to fail builds when tests pass but performance degrades or error logs contain critical messages.
- Tag and pin important builds: Use build tags to mark releases and pin builds to preserve their artifacts from cleanup policies.
- Review audit logs: Periodically review the audit log to track configuration changes and administrative actions.
Conclusion
TeamCity is a mature and feature-rich CI/CD platform that scales from simple build automation to complex, multi-stage deployment pipelines. By understanding its core concepts, leveraging the Kotlin DSL for configuration-as-code, and following best practices around security, performance, and agent management, development teams can build a reliable and efficient delivery workflow. Start with a basic build configuration, gradually add testing and deployment stages, and refine your pipeline over time as your project grows. With proper configuration, TeamCity becomes an indispensable tool that keeps your codebase healthy and your releases predictable.