Buildkite: Scalable CI with Your Own Agents

Khimananda Oli 8 min read Database
Buildkite: Scalable CI with Your Own Agents

By Khimananda Oli | Last reviewed: August 2026

Teams hitting the ceiling of managed CI runners often turn to Buildkite: Scalable CI with Your Own Agents to regain control over performance, security, and cost. Unlike fully SaaS platforms where you rent ephemeral compute, Buildkite separates the orchestration plane from the execution plane, letting you run build agents on your own infrastructure while keeping pipeline logic centralized. This hybrid model is particularly valuable for organizations in Nepal and globally that require air-gapped environments, specific hardware access, or strict data residency compliance without sacrificing modern developer experience.

Buildkite Control Plane (Managed)Pipeline Orchestration • API • UI • Audit LogsAWS Elastic Agent PoolEC2 Spot FleetEKS / ECS TasksAuto Scaling GroupOn-Prem / Private CloudBare Metal GPUAir-Gapped VMsInternal RegistryHTTPS Outbound OnlySecure Tunnel / NAT
Buildkite: Scalable CI with Your Own Agents separates the managed control plane from self-hosted execution pools in cloud and on-premise environments.

How does Buildkite: Scalable CI with Your Own Agents differ from pure SaaS?

The fundamental difference lies in the execution boundary. In platforms like GitHub Actions or CircleCI, the default runners are multi-tenant VMs managed by the vendor. You can bring your own runners, but they are often treated as secondary options. With Buildkite, self-hosted agents are the primary abstraction. The Buildkite API serves only as a scheduler and state store; it never touches your source code, artifacts, or secrets during job execution unless you explicitly upload them.

This distinction matters for three practical reasons I encounter frequently when consulting on self-hosted CI runner security:

  • Network topology: Agents initiate outbound HTTPS connections to the Buildkite API. No inbound ports need opening, making it viable behind restrictive corporate firewalls or in Nepal-based data centers with limited ingress rules.
  • Hardware affinity: You can tag agents with capabilities like gpu=true, arch=arm64, or fpga=yes. Pipelines target these tags, ensuring ML training jobs land on expensive GPUs while linting runs on cheap spot instances.
  • Data gravity: For teams with terabyte-scale datasets or proprietary models, moving data to a SaaS runner is cost-prohibitive and slow. Self-hosted agents execute adjacent to your storage, reducing build times from hours to minutes.

A common mistake is treating Buildkite agents as static pets. In production, you should treat them as cattle—ephemeral, auto-scaled, and replaced after every job or on a short cadence to prevent configuration drift and maintain reproducibility.

How do you configure elastic scaling for Buildkite agents on AWS?

Static agent fleets waste money during quiet periods and throttle builds during peaks. The AWS Auto Scaling integration with Buildkite solves this through the Elastic CI Stack, a CloudFormation template maintained by Buildkite that provisions an Auto Scaling Group (ASG) driven by queue depth metrics rather than CPU utilization.

Deploy the Elastic CI Stack

The most reliable method uses the official CloudFormation template. Avoid manual ASG setup unless you have specific networking constraints.

# Clone the elastic-ci-stack-for-aws repository
git clone https://github.com/buildkite/elastic-ci-stack-for-aws.git
cd elastic-ci-stack-for-aws

# Deploy with required parameters
aws cloudformation create-stack \
  --stack-name buildkite-agents-prod \
  --template-url https://s3.amazonaws.com/buildkite-aws-stack/latest/aws-stack.yml \
  --parameters \
    ParameterKey=BuildkiteAgentToken,ParameterValue=YOUR_AGENT_TOKEN \
    ParameterKey=BuildkiteQueueTag,ParameterValue=default \
    ParameterKey=InstanceType,ParameterValue=m7g.large \
    ParameterKey=MaxSize,ParameterValue=20 \
    ParameterKey=MinSize,ParameterValue=0 \
    ParameterKey=VpcId,ParameterValue=vpc-0abc123def456 \
    ParameterKey=Subnets,ParameterValue=subnet-aaa,subnet-bbb \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

Tune scaling responsiveness

The default scaling policy reacts conservatively. For fast-moving teams, adjust the CloudWatch alarm thresholds. The key metric is BuildkiteJobQueueDepth. Set the scale-up threshold to trigger when pending jobs exceed available agents for more than 60 seconds, not 300. Scale-down should use a longer cooldown (typically 10–15 minutes) to avoid thrashing during commit bursts.

For Kubernetes-native teams, the Amazon EKS practical guide covers similar patterns using the Buildkite Agent Helm chart with KEDA scalers, which often provides faster scale-up times than EC2 ASGs due to pod scheduling speed versus instance boot time.

Buildkite APIQueue MetricsCloudWatchAlarm & TargetAuto ScalingGroup PolicyEC2 / EKS NodesAgent InstancesPublishTriggerScale Up/DownFeedback LoopNew agents register → Queue drains → Alarm resets
Elastic scaling loop: Buildkite queue depth drives CloudWatch alarms that adjust the Auto Scaling Group, creating a responsive feedback cycle.

How do you secure self-hosted Buildkite agents for compliance?

Running your own agents means owning the security boundary. For SOC 2 or ISO 27001 audits, you must demonstrate that build environments cannot leak secrets, persist malicious state, or access unauthorized resources. I apply these controls consistently across client engagements requiring automated SOC 2 compliance evidence.

Isolate workloads with disposable environments

Never run untrusted code directly on the host OS. Use one of these isolation strategies:

  1. Docker plugin (recommended): Each step runs in a fresh container. Mount only necessary volumes. Use read-only root filesystems where possible.
  2. Kubernetes executor: Each job gets a dedicated pod with network policies restricting egress. Ideal for multi-tenant clusters.
  3. VM reset: For bare-metal or legacy workloads, snapshot the clean state and revert after each job. Slower but provides strongest isolation.

Manage secrets without environment variables

Injecting secrets as environment variables exposes them in process listings and logs. Instead, integrate with HashiCorp Vault or AWS Secrets Manager using the Buildkite secrets plugin. Fetch credentials at runtime inside the isolated container, write them to memory-backed tmpfs, and clean up immediately after use. This pattern satisfies auditors who flag static env vars as findings.

Restrict agent capabilities with tags

Use agent tags to enforce least privilege. Tag agents with env=production or compliance=pci and configure pipeline steps to target only matching agents. Never allow a development-tagged agent to pick up production deployment jobs. This logical separation is critical when sharing infrastructure across teams.

When should you choose Buildkite over GitHub Actions or GitLab CI?

Choosing between CI platforms is rarely about features alone—it's about operational fit. This comparison reflects real-world trade-offs I've navigated when migrating teams from managed to hybrid systems.

CriteriaBuildkite (Self-Hosted)GitHub ActionsGitLab CI
Execution ModelHybrid: Managed orchestration + your infraSaaS default; self-hosted optionalSaaS or self-managed runners
Scaling SpeedSeconds (K8s) to minutes (EC2 ASG)Instant (SaaS); variable (self-hosted)Variable; depends on runner config
VPC / Private AccessNative; agents live inside your networkRequires tunneling or enterprise planPossible with self-managed runners
Pipeline ConfigYAML + Bash hooks; highly extensibleYAML workflows; reusable actionsYAML .gitlab-ci.yml; includes/extends
Cost ModelPer-user fee + your infra costsPer-minute (SaaS) or free (self-hosted)Per-user or per-minute; self-hosted free
Compliance FitExcellent; full infra control + audit logsLimited on SaaS; better with GHESGood with self-managed; complex licensing
Best ForComplex, secure, hybrid, or specialized hardwareOpen source, standard web apps, GitHub-centricAll-in-one DevOps platform preference

Choose Buildkite when your bottleneck is infrastructure control, not feature parity. If you need GPU clusters, on-premise database access, or deterministic build environments for regulated industries, Buildkite’s agent-first design wins. For standard web app CI where convenience trumps customization, GitHub Actions remains hard to beat. GitLab CI fits teams already invested in the GitLab ecosystem who want integrated MR workflows without stitching tools together.

Infrastructure Control & Compliance DepthDeveloper Convenience & SpeedGitHub ActionsHigh ConvenienceGitLab CIBalancedBuildkiteMax ControlIdeal Zone for Buildkite• Regulated Industries• Specialized Hardware• Air-Gapped Networks
Positioning CI platforms: Buildkite occupies the high-control quadrant where compliance and infrastructure specificity outweigh pure convenience.

What are common pitfalls when operating Buildkite agents at scale?

After deploying Buildkite across multiple production environments, these issues surface repeatedly:

  • Ignoring agent draining: When an ASG terminates an instance mid-job, builds fail. Always enable the --disconnect-after-job flag or use the lifecycle hook script provided by the Elastic CI Stack to gracefully drain agents before shutdown.
  • Over-provisioning instance types: Using m7g.xlarge for everything wastes money. Profile your builds. Most lint/test jobs fit on m7g.medium. Reserve larger instances for compilation-heavy or integration test steps. Use agent tags to route appropriately.
  • Neglecting log retention: Buildkite stores logs in its managed service, but for compliance you may need local copies. Configure the S3 log upload plugin early. Retroactively adding it creates gaps in audit trails.
  • Hardcoding agent tokens: Rotate tokens regularly. Store them in Secrets Manager and inject via user-data scripts, not AMI bakes. Baked tokens become stale and create rotation headaches.

Monitoring is non-negotiable. Expose agent metrics via the Prometheus exporter and build dashboards tracking queue wait time, agent utilization, and job duration. Without observability, you're guessing at capacity needs. Refer to Prometheus metrics monitoring fundamentals if you're setting up this telemetry stack for the first time.

Getting started with Buildkite: Scalable CI with Your Own Agents

Start small: deploy a single Elastic CI Stack in a non-production AWS account, migrate one low-risk pipeline, and validate the scaling behavior under load before expanding. Document your agent tagging strategy and secret management approach upfront—retrofitting these later causes migration pain. If you're evaluating whether Buildkite fits your team's compliance requirements or infrastructure constraints, or need help designing a secure agent architecture that passes audit, reach out to discuss your specific CI/CD challenges.

Frequently Asked Questions

Buildkite is a CI orchestration platform where you host your own build agents on AWS, GCP, or bare metal. Unlike GitHub Actions, compute runs entirely in your infrastructure, giving you full control over security, networking, and hardware while Buildkite manages only the scheduling and UI layer.

Add the official Buildkite APT repository, install the buildkite-agent package via apt, then configure the token in /etc/buildkite-agent/buildkite-agent.cfg. Start the service with systemctl enable --now buildkite-agent. The agent automatically registers with your organization and begins polling for jobs immediately after configuration.

No, Buildkite charges per user seat and pipeline concurrency, not build minutes. You pay for your own cloud instances or bare metal servers separately. This model makes costs predictable for high-volume teams running thousands of builds daily on self-hosted infrastructure.

Yes, use the official Docker plugin to isolate builds in containers. Enable rootless Docker or run agents as non-root users to prevent privilege escalation. Mount volumes read-only when possible and use ephemeral agents that terminate after each job to minimize container escape risks.

Deploy the elastic-ci-stack-for-aws CloudFormation template which provisions an Auto Scaling Group tied to SQS queue depth. Agents scale up when pending jobs exceed thresholds and terminate after idle periods. Configure instance types, spot preferences, and VPC settings directly in the stack parameters.

Integrate HashiCorp Vault or AWS Secrets Manager using the secrets plugin rather than storing credentials in environment variables. Fetch secrets at runtime during pipeline steps so sensitive values never persist on disk. Rotate credentials centrally without updating individual agent configurations or restarting services.

Check network connectivity to api.buildkite.com on port 443 and verify firewall rules allow outbound HTTPS. Confirm the agent token is valid in the dashboard under Agents. Review journalctl logs for authentication errors or rate limiting messages that indicate misconfigured credentials or IP blocking.

Use the cache plugin with S3 or GCS backends to store node_modules, vendor directories, or Docker layers between builds. Define cache keys based on lockfile hashes to invalidate stale entries. Self-hosted agents benefit significantly since they avoid repeated downloads from public registries.

Yes, deploy agents using the official Helm chart which creates Jobs or Deployments depending on your scaling preference. Configure service accounts with minimal RBAC permissions and use pod security standards to restrict capabilities. Agents register dynamically and terminate pods after completing assigned workloads.

Install buildkite-cli and run bk local execute to simulate pipeline steps on your machine. This validates command syntax, plugin configuration, and artifact paths without consuming remote agent capacity. Note that some plugins requiring cloud credentials may need mock endpoints for accurate local testing.

Use compute-optimized instances like c7g.xlarge or c6i.xlarge for PHP compilation and testing. Allocate at least 4GB RAM per concurrent job to prevent OOM failures during Composer installs. EBS gp3 volumes provide consistent IOPS for database migrations and asset compilation without burst throttling.

Pin plugin versions to specific commits instead of tags, enable signature verification for custom plugins, and run agents in isolated VPCs without public internet access except through egress proxies. Audit third-party hooks regularly and use immutable base images rebuilt monthly with verified checksums.

Yes, define matrix attributes in your pipeline.yml to generate parallel jobs across PHP versions, databases, or OS variants. Each combination runs as a separate job distributed across available agents. Combine with conditional steps to skip irrelevant combinations and reduce total execution time significantly.

Enable Prometheus metrics exporter on agents and scrape endpoints with Grafana dashboards tracking job duration, queue wait times, and failure rates. Set alerts for sustained queue backlog or agent disconnections. Correlate metrics with CloudWatch or Datadog to identify infrastructure bottlenecks affecting build throughput.

Buildkite automatically retries failed jobs on other available agents if retry limits permit. Configure automatic retries for transient failures like network timeouts but require manual intervention for test failures. Orphaned artifacts remain accessible via the API even if the original agent terminates unexpectedly during upload.