Bitbucket Pipelines self-hosted runners: Docker, shell, Windows, macOS and autoscaling

A Bitbucket Pipelines self-hosted runner is a process on your own machine that pulls steps from Bitbucket Cloud and executes them. You create the runner in workspace or repository settings, run the command Bitbucket generates (a docker container run on Linux), and send steps to it with runs-on: [self.hosted, linux] in bitbucket-pipelines.yml.

Runner types and when to self-host

Bitbucket Cloud offers five self-hosted runner flavours. The table lists the differences that decide which one you install.

RunnerExecutes steps inSystem labelsDocker features
Linux Docker (x86_64)A fresh container per stepself.hosted, linuxFull: services, pipes, custom images
Linux Docker (arm64)A fresh container per stepself.hosted, linux.arm64Full
Linux shellBash on the hostself.hosted, linux.shellNo services, pipes or custom images
WindowsPowerShell on the hostself.hosted, windowsNo services or pipes
macOSShell on the hostself.hosted, macosNo services or pipes

Self-hosting pays off when you need more than the 4x (16 GB) ceiling of Atlassian's cloud runners, a private network path to databases or artifact stores, a GPU, macOS or Windows builds, or warm caches on local disk. Steps on your own runners do not consume Bitbucket build minutes. You pay for the hardware instead, plus concurrency fees if you turn on Premium Runners (see Limits and costs).

Prerequisites

  • Workspace or repository admin rights in Bitbucket Cloud.
  • Linux Docker runner: a 64-bit Linux host with at least 8 GB of RAM and Docker 19.03 or newer. Give the runner container itself at least 512 MB.
  • Linux shell and macOS runners: 64-bit host, 8 GB RAM, Git 2.35+, and OpenJDK 25 (25.0.2 or newer) for runner v6. Runners below v6 need OpenJDK 11.0.15+. The shell runner also needs Bash 3.2+.
  • Windows runner: Windows 10+ or Windows Server 2019+, 8 GB RAM, PowerShell 5.0+, Git 2.35+ and the same OpenJDK versions.
  • Outbound HTTPS from the host to Bitbucket and to docker-public.packages.atlassian.com, where Atlassian publishes the runner image.

Workspace vs repository runners

You create runners in one of two places:

  • Workspace runners (Workspace settings, Pipelines, Runners) accept steps from any repository in the workspace. Use them for a shared fleet.
  • Repository runners (Repository settings, Pipelines, Runners) serve one repository. Use them when a project needs special hardware or a network segment that other teams must not reach.

The free runner tier allows up to 100 runners per workspace and 100 per repository. A repository runner's generated command contains an extra REPOSITORY_UUID variable, and that is the only difference on the host side.

Install a Linux Docker runner

  1. Open Runners in workspace or repository settings and choose Add runner.
  2. Pick Linux Docker (x86) or the arm64 variant, give the runner a name and add up to 10 custom labels.
  3. Copy the command from the Run step of the dialog and store the OAuth client secret in your secret manager. Copy the whole secret: it can contain special characters.
  4. Run the command on the host. The runner status in Bitbucket switches from Unregistered to Online.

The generated command looks like this (UUIDs and secrets replaced):

docker container run -it \
  -v /tmp:/tmp \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  -e ACCOUNT_UUID={workspace-uuid} \
  -e REPOSITORY_UUID={repository-uuid} \
  -e RUNNER_UUID={runner-uuid} \
  -e RUNTIME_PREREQUISITES_ENABLED=true \
  -e OAUTH_CLIENT_ID=<client-id> \
  -e OAUTH_CLIENT_SECRET=<client-secret> \
  -e WORKING_DIRECTORY=/tmp \
  --name runner-<runner-uuid> \
  docker-public.packages.atlassian.com/sox/atlassian/bitbucket-pipelines-runner

Workspace runners omit REPOSITORY_UUID. The runner talks to the host Docker daemon through the mounted socket and starts one build container per step, plus any service containers. It reads container logs from /var/lib/docker/containers, which is why that mount exists.

For production, replace -it with -d --restart unless-stopped so the runner survives reboots. If you move the working directory off /tmp, mount it at the same path inside and outside the container (-v /mydir:/mydir -e WORKING_DIRECTORY=/mydir); the build containers resolve paths against the host.

To upgrade, stop and remove the container, run docker image pull docker-public.packages.atlassian.com/sox/atlassian/bitbucket-pipelines-runner and start it again with the same command.

Linux shell, Windows and macOS runners

For these three, the dialog offers a download (a tar.gz on Linux and macOS, a zip on Windows). You extract it, open the bin directory and run the start command that the dialog prints, which passes the same account UUID, runner UUID and OAuth credentials as arguments. On Windows, open PowerShell as administrator. Run it under a service manager (systemd, launchd or a Windows service wrapper) so it restarts after a crash.

Shell, Windows and macOS runners execute steps on the host, so files, installed packages and running processes persist between steps. Clean the working directory at the start of each step and keep these hosts on a single trusted repository.

Labels and runs-on

A step goes to a runner only if that runner carries every label in the step's runs-on list. Custom labels may contain lowercase letters, digits and dots.

pipelines:
  default:
    - step:
        name: Build on own hardware
        runs-on:
          - self.hosted
          - linux
          - gpu.a10
        size: 4x
        script:
          - nvidia-smi
          - make test
    - step:
        name: Lint on Atlassian cloud runners
        script:
          - make lint

Steps without runs-on keep running on Atlassian's infrastructure, so you can mix both in one pipeline. If no online runner matches all labels, the step fails. If matching runners exist but all are busy, the step waits in the queue.

Step sizes and memory

Linux Docker runners accept size: 1x, 2x, 4x and 8x, which give the step 4, 8, 16 and 32 GB of memory. The host must have that much free, plus overhead for the runner container. Service containers declare their own memory, and that memory comes out of the step's total:

definitions:
  services:
    docker:
      memory: 4096
    postgres:
      image: postgres:16
      memory: 1024

The Linux shell runner ignores size limits; its steps use whatever memory the host has. Each step can run for up to 120 minutes before Bitbucket times it out.

Autoscaling on Kubernetes

Atlassian publishes the Runners Autoscaler for Kubernetes in the bitbucketpipelines/runners-autoscaler repository. It polls the Bitbucket API, creates runners through the API, and starts each one as a Kubernetes Job. A companion deployment, the cleaner, deletes unhealthy runners and their jobs.

You deploy it with Kustomize (kubectl apply -k values) after editing runner_config.yaml:

constants:
  runner_api_polling_interval: 600
  runner_cool_down_period: 300
groups:
  - name: "Linux builders"
    workspace: "{workspace-uuid}"
    labels:
      - "k8s.builder"
    namespace: "default"
    strategy: "percentageRunnersIdle"
    parameters:
      min: 2
      max: 20
      scale_up_threshold: 0.5
      scale_down_threshold: 0.2
      scale_up_multiplier: 1.5
      scale_down_multiplier: 0.5

The strategy compares busy runners with online runners. Above scale_up_threshold it adds runners, below scale_down_threshold it removes idle ones. The autoscaler authenticates with either an OAuth consumer or an API token with the read:repository:bitbucket, read:workspace:bitbucket, read:runner:bitbucket and write:runner:bitbucket scopes, stored as a Kubernetes secret.

Keep the trade-offs in mind. The autoscaler scales Linux Docker runners inside a cluster you already run; node autoscaling stays your job. A 600-second default polling interval means a burst of commits waits minutes before new runners appear, and lowering it below 120 seconds risks API rate limits. Runners inside Kubernetes also need Docker-in-Docker for build containers, which means privileged pods.

Security hardening

  • Treat the OAuth client secret like a password. Anyone holding it can register a runner and receive your steps, including their secured variables.
  • The Docker runner mounts the host Docker socket. Any step can start a privileged container and own the host. Dedicate the host to CI and keep nothing else on it.
  • Bitbucket does not run pipelines for pull requests from forks, which removes the most common untrusted-code path. Anyone with write access can still change bitbucket-pipelines.yml, and anyone with write access can read secured variables.
  • Split runners by trust with labels: production deploy steps go to deploy.prod runners on a locked-down network, everything else to general builders.
  • Recycle hosts. A Docker runner that has run hundreds of steps carries leftover images, volumes and caches from all of them.

Troubleshooting

Step fails with no matching runner

Bitbucket fails steps when no online runner carries every label in runs-on. Check spelling, check that the runner shows Online, and confirm that you created a repository runner in the right repository.

Container name conflict on restart

The generated command names the container runner-<uuid>. Remove the old one with docker container rm -f runner-<uuid> before starting again.

Runner cannot read container logs

Errors about /var/lib/docker/containers come from missing read permission (sudo chmod +r /var/lib/docker/containers), a logging driver other than json-file, or Docker user-namespace remapping. Switch the daemon to json-file and turn remapping off.

Image pull blocked

Corporate proxies often block docker-public.packages.atlassian.com. Allow it, or mirror the image into your registry and change the image reference.

Windows runner will not start

PowerShell's execution policy blocks the unsigned start script. Allow it for the runner directory, then rerun the command from an elevated prompt.

Limits and costs

Since June 2026 Bitbucket offers two runner tiers. Free runners keep the 100-runner limit and community support. Premium Runners add runner groups, auto-update, custom resources and queue management, and bill $15 per month per concurrent build slot; Standard plans include one slot and Premium plans two. With Premium Runners on, Bitbucket queues steps above your slot limit, up to 1,000 queued steps per workspace.

The bigger cost is your fleet. A runner host sized for 8x steps needs 32 GB of free memory, and if it sits idle overnight you pay for it anyway. Autoscaling cuts the idle hours but adds a Kubernetes cluster to maintain, upgrade and patch. Count the engineering hours spent on runner images, disk cleanup and stuck steps alongside the VM bill.

FAQ

Do Bitbucket self-hosted runners use build minutes?

No. Steps on your runners do not draw from the workspace's build minutes. Premium Runners carry a separate per-slot concurrency fee.

Can I run Bitbucket runners on Kubernetes without the autoscaler?

Yes. You can deploy the runner image as a pod with the ACCOUNT_UUID, RUNNER_UUID and OAuth variables set and a Docker-in-Docker sidecar. The autoscaler adds the part that creates and deletes runners for you.

Is there an ephemeral, one-step-per-runner mode?

Bitbucket does not document a flag that makes a runner exit after one step. The Linux Docker runner gives each step a fresh build container, and the autoscaler replaces runners over time. For a fresh VM per step you need your own orchestration.

Can one pipeline use both cloud and self-hosted runners?

Yes. Add runs-on to the steps that need your hardware and leave it off the rest.

Other platforms follow similar patterns: compare self-hosted GitHub Actions runners and GitLab CI custom runners, or the agent model in Azure DevOps self-hosted agents.

Running runners with cirunner.dev

cirunner.dev provisions a fresh VM per job, registers it with your CI using a token you provide, scales with the queue and destroys the machine after the job. You pick CPU, RAM, x86_64 or arm64, region, base image and an optional GPU, and pay per compute minute.

The service is in early access with GitHub Actions and GitLab CI as launch platforms. Bitbucket Pipelines is on the roadmap; vote for it on the early-access form.

Skip the runner fleet

cirunner.dev boots a fresh VM for every Bitbucket Pipelines job, registers it, and destroys it when the job ends. Bitbucket Pipelines is on our roadmap. Vote for it on the early-access form.

Join early access