Reducing YAML Bloat in Bitbucket Pipelines Using Pipes
Stop writing fragile bash scripts in your CI/CD. Learn how to use Bitbucket Pipes to modularize deployments to AWS, Azure, and more while keeping your YAML clean.
09 Jul 2025, 11:15 UTC

The Problem: The Scripting Rabbit Hole
When setting up a CI/CD pipeline, it is tempting to write custom shell scripts for every deployment task. You start with a simple curl command to trigger a webhook, but soon you are managing SSH keys, handling API authentication retries, and debugging environment‑specific shell differences inside a Docker container. This leads to a bloated bitbucket-pipelines.yml file that is difficult to maintain and prone to breaking when the target cloud provider updates their CLI.
The solution is to shift from custom scripting to Pipes. Pipes are pre‑packaged Docker containers provided by Bitbucket or third parties that encapsulate the logic for common tasks. Instead of writing twenty lines of bash to upload a folder to an S3 bucket, you call a single pipe that handles the authentication and transfer logic for you.
How Pipes Simplify the Workflow
A pipe acts as a modular function for your pipeline. Rather than installing dependencies (like the AWS CLI or Azure CLI) manually in every step, the pipe brings its own environment. This ensures that the version of the tool used for deployment is consistent and managed.
Key Components of a Pipe‑Based Pipeline
- The YAML Configuration: The
bitbucket-pipelines.ymlfile defines the sequence of events. - Deployment Environments: These are logical groupings (e.g., 'Staging', 'Production') that allow you to track which commit is live and restrict who can trigger the deployment.
- Artifacts: Since each step in a pipeline runs in a fresh container, artifacts allow you to pass the compiled build (like a
/distfolder) from a build step to a deployment pipe.
Worked Example: Deploying a Static Site to AWS S3
In this scenario, we assume a project where a build step generates static HTML files that need to be synced to an S3 bucket. This requires the aws-s3-deploy pipe.
Prerequisites: You must define AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as masked variables in Repository settings > Pipelines > Repository variables to avoid exposing credentials in the code.
image: node:18 # Base image for the build step
pipelines:
branches:
master:
- step:
name: Build and Test
caches:
- node
script:
- npm install
- npm run build
artifacts:
- dist/** # Pass the build output to the next step
- step:
name: Deploy to Production
deployment: Production # Tracks deployment in the UI
script:
- pipe: atlassian/aws-s3-deploy:1.1.0
variables:
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
AWS_S3_BUCKET: 'my-production-bucket'
LOCAL_PATH: 'dist'
Execution Details
- Run Location: This configuration is processed by the Bitbucket Pipelines runner.
- Permissions: The IAM user associated with the access keys must have
s3:PutObjectands3:ListBucketpermissions for the specified bucket. - Expected Result: The 'Build and Test' step creates the
distfolder, which is then picked up by theaws-s3-deploypipe and synced to S3.
Trade‑offs and Limitations
While pipes reduce maintenance, they introduce a dependency on the pipe provider. If a pipe version is deprecated or contains a bug, you are reliant on the maintainer for a fix. Additionally, using multiple heavy pipes can increase the total pipeline duration because each pipe requires pulling a separate Docker image.
Performance Tip: To minimize startup time, use the most specific version of a pipe (e.g., 1.1.0) rather than latest. This prevents the pipeline from checking for updates on every run and ensures build reproducibility.
Verifying the Deployment
To confirm the pipe worked correctly without manually checking the cloud console:
- Check the Deployments tab in the Bitbucket sidebar to see if the 'Production' environment updated to the latest commit.
- Review the pipeline logs for the
aws-s3-deploystep; it should explicitly list the files uploaded to the bucket. - If the deployment fails, verify that the
LOCAL_PATHvariable matches the path defined in theartifactssection of the previous step.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.