> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Buildkite Stack

> Deploy a Buildkite CI/CD stack on AWS with Fjall, auto-scaling EC2 agents and managed compute.

The Buildkite pattern is a standalone CDK construct (`Buildkite`) that provisions a complete CI/CD stack on AWS. Compose it into your own CDK app and deploy with `cdk deploy`. It is not a `fjall create` option.

## Prerequisites

* Node 22 or later
* AWS CDK installed (`npm install -g aws-cdk`)
* `@fjall/components-infrastructure` added to your project
* A Buildkite agent token from your Buildkite organisation settings

## What it provisions

| Component            | Detail                                                              |
| -------------------- | ------------------------------------------------------------------- |
| EC2 Buildkite agents | Auto Scaling Group (configurable min/max instances)                 |
| Spot capacity        | Configurable percentage of spot instances for cost savings          |
| S3 bucket            | Build artifact storage                                              |
| SSM Parameter Store  | Agent token as an encrypted SecureString at `/buildkite/agentToken` |
| KMS key              | Dedicated key for token encryption                                  |
| CloudWatch logs      | Agent log streams with configurable retention                       |
| VPC                  | Public subnet with scoped security groups                           |

Agents poll the Buildkite API for jobs, so the stack creates no load balancer.

## Quick start

Instantiate the construct in your CDK app:

```typescript theme={null}
import { App } from "aws-cdk-lib";
import {
  Buildkite,
  BuildkiteDefaultProps,
} from "@fjall/components-infrastructure";

const app = new App();

new Buildkite(app, "BuildkiteStack", {
  ...BuildkiteDefaultProps,
  env: { account: "123456789012", region: "us-east-1" },
  tags: {},
});

app.synth();
```

`BuildkiteDefaultProps` supplies sensible defaults for every required field, so spread it first and override only what you need.

Set the agent token in your shell before synthesis, then deploy:

```bash theme={null}
export BUILDKITE_AGENT_TOKEN="your-agent-token"
cdk deploy BuildkiteStack
```

<Note>
  The agent token is read at synthesis time from the `BUILDKITE_AGENT_TOKEN` environment variable and written to AWS SSM Parameter Store as a SecureString at `/buildkite/agentToken`, encrypted with a dedicated KMS key. If you skip the environment variable, set the parameter value in SSM after deployment.
</Note>

## Configuration

Override defaults by passing props alongside the `BuildkiteDefaultProps` spread:

```typescript theme={null}
import { InstanceType, InstanceClass, InstanceSize } from "aws-cdk-lib/aws-ec2";

new Buildkite(app, "BuildkiteStack", {
  ...BuildkiteDefaultProps,
  env: { account: "123456789012", region: "us-east-1" },
  tags: {},
  agentsPerInstance: 1,
  agentMinInstances: 1,
  agentMaxInstances: 10,
  agentInstanceType: InstanceType.of(InstanceClass.M5, InstanceSize.LARGE),
  spotCapacityPercentage: 100,
});
```

### Core parameters

| Parameter                | Type           | Description                             | Default     |
| ------------------------ | -------------- | --------------------------------------- | ----------- |
| `agentsPerInstance`      | number         | Buildkite agents per EC2 instance       | `1`         |
| `agentMinInstances`      | number         | Minimum EC2 instances                   | `0`         |
| `agentMaxInstances`      | number         | Maximum EC2 instances                   | `10`        |
| `agentInstanceType`      | `InstanceType` | EC2 instance type (CDK `InstanceType`)  | `m5.large`  |
| `spotCapacityPercentage` | number         | Percentage of spot instances (0 to 100) | `75`        |
| `buildkiteQueue`         | string         | Queue name for agents                   | `"default"` |
| `buildkiteAgentTags`     | string         | Agent metadata tags                     | `""`        |
| `keyName`                | string         | SSH key pair name for SSH access        | `""`        |

### Agent configuration

| Parameter                            | Type           | Description                                      | Default  |
| ------------------------------------ | -------------- | ------------------------------------------------ | -------- |
| `buildkiteAgentRelease`              | `AgentRelease` | Agent release channel (`stable`, `beta`, `edge`) | `stable` |
| `buildkiteAgentTimestampLines`       | boolean        | Add timestamps to log lines                      | `false`  |
| `buildkiteAgentExperiments`          | string         | Comma-separated experimental features            | `""`     |
| `buildkiteAgentCancelGracePeriod`    | number         | Seconds to wait before force-killing jobs        | `60`     |
| `buildkiteTerminateInstanceAfterJob` | boolean        | Terminate instance after job completes           | `false`  |
| `buildkiteAdditionalSudoPermissions` | string         | Additional sudo permissions for the agent        | `""`     |
| `buildkiteAgentEnableGitMirrors`     | boolean        | Enable Git mirrors for faster clones             | `false`  |

### Scaling configuration

| Parameter                     | Type    | Description                                      | Default      |
| ----------------------------- | ------- | ------------------------------------------------ | ------------ |
| `scalerEventSchedulePeriod`   | string  | How often to check for scaling events            | `"1 minute"` |
| `scalerMinPollInterval`       | string  | Minimum interval between scaling checks          | `"10s"`      |
| `scaleOutFactor`              | string  | Factor for scaling out (`"1.0"` is linear)       | `"1.0"`      |
| `scaleInIdlePeriod`           | number  | Seconds an instance must be idle before scale-in | `600`        |
| `scaleOutWaitingForJobs`      | boolean | Scale out when jobs are waiting                  | `false`      |
| `buildkiteAgentScalerVersion` | string  | Version of the auto-scaler                       | `"1.8.0"`    |

### Storage and logging

| Parameter               | Type    | Description                             | Default |
| ----------------------- | ------- | --------------------------------------- | ------- |
| `agentVolumeSize`       | number  | EBS volume size in GB                   | `250`   |
| `logRetentionDays`      | number  | CloudWatch log retention in days        | `1`     |
| `enableInstanceStorage` | boolean | Use instance storage instead of EBS     | `false` |
| `mountTmpfsAtTmp`       | boolean | Mount tmpfs at `/tmp` for faster builds | `true`  |

### Docker configuration

| Parameter                        | Type    | Description                                         | Default  |
| -------------------------------- | ------- | --------------------------------------------------- | -------- |
| `enableEcrPlugin`                | boolean | Enable the ECR plugin for Docker images             | `true`   |
| `enableDockerLoginPlugin`        | boolean | Enable the Docker Hub login plugin                  | `true`   |
| `enableSecretsPlugin`            | boolean | Enable the secrets plugin                           | `true`   |
| `enableDockerUserNamespaceRemap` | boolean | Enable user-namespace remapping                     | `true`   |
| `enableDockerExperimental`       | boolean | Enable Docker experimental features                 | `false`  |
| `dockerNetworkingProtocol`       | string  | Docker networking protocol (`ipv4` or `dualstack`)  | `"ipv4"` |
| `ecrAccessPolicy`                | string  | ECR access policy (`readonly`, `poweruser`, `full`) | `""`     |

### Security and networking

| Parameter                                    | Type   | Description                                      | Default   |
| -------------------------------------------- | ------ | ------------------------------------------------ | --------- |
| `authorizedUsersUrl`                         | string | URL to an authorised SSH users file              | `""`      |
| `bootstrapScriptUrl`                         | string | URL to a custom bootstrap script                 | `""`      |
| `agentEnvFileUrl`                            | string | URL to an environment file for agents            | `""`      |
| `pipelineSigningKMSKey`                      | string | KMS key ARN for pipeline signing                 | `""`      |
| `pipelineSigningVerificationFailureBehavior` | string | Behaviour on signature failure (`warn`, `block`) | `"block"` |

### Advanced parameters

| Parameter                      | Type   | Description                                  | Default     |
| ------------------------------ | ------ | -------------------------------------------- | ----------- |
| `elasticStackVersion`          | string | Version of the Elastic CI Stack              | `"v6.30.0"` |
| `buildkiteAgentTracingBackend` | string | Tracing backend (`datadog`, `opentelemetry`) | `""`        |

## Cost optimisation

The pattern includes several cost-saving features:

* **Spot instances**: save up to 90% on compute costs.
* **Auto-scaling**: pay only for the agents you run.
* **Configurable retention**: keep CloudWatch logs only as long as you need them.

Estimated monthly cost is **$50 to $200** depending on usage.

## Customisation

### Queue-based agents

Target a specific Buildkite queue:

```typescript theme={null}
new Buildkite(app, "BuildkiteStack", {
  ...BuildkiteDefaultProps,
  env: { account: "123456789012", region: "us-east-1" },
  tags: {},
  buildkiteQueue: "deploy",
  buildkiteAgentTags: "queue=deploy,env=production",
});
```

### Docker and ECR integration

Enable ECR and Docker plugins for container workflows:

```typescript theme={null}
new Buildkite(app, "BuildkiteStack", {
  ...BuildkiteDefaultProps,
  env: { account: "123456789012", region: "us-east-1" },
  tags: {},
  enableEcrPlugin: true,
  enableDockerLoginPlugin: true,
  enableSecretsPlugin: true,
});
```

### SSH access for debugging

Attach an existing EC2 key pair for SSH access:

```typescript theme={null}
new Buildkite(app, "BuildkiteStack", {
  ...BuildkiteDefaultProps,
  env: { account: "123456789012", region: "us-east-1" },
  tags: {},
  keyName: "my-ssh-key",
});
```

## Monitoring

Agent logs flow to CloudWatch with the retention set by `logRetentionDays`. View instance health, scaling activity, and agent logs in the AWS console under EC2 Auto Scaling Groups and CloudWatch Logs for the stack's region.

## Next Steps

<CardGroup cols={2}>
  <Card title="Compute Factory" icon="microchip" href="/patterns/compute-factory">
    Configure ECS and Lambda compute
  </Card>

  <Card title="Storage Factory" icon="box-archive" href="/patterns/storage-factory">
    Configure S3 buckets for artifacts
  </Card>
</CardGroup>
