CI/CD for Ruby on Rails with Atlassian Bamboo
I'll be upfront: Bamboo wasn't my choice. When I came in at a healthcare tech company to help manage their Rails CI/CD pipelines, Bamboo was already there. The engineering team was already on Jira and Confluence, the Bamboo server was already running, and switching to Jenkins or CircleCI wasn't on the table. So I learned Bamboo well enough to make it work, and there's more to say about it than "it's not Jenkins."
This is what CI/CD for a Rails application on Bamboo actually looks like.
Bamboo Concepts
Bamboo organizes work in a strict hierarchy: Plans contain Stages, Stages contain Jobs, Jobs contain Tasks. Understanding this matters because the mental model is different from Jenkins pipelines or GitHub Actions.
A Plan is the top-level pipeline — one Plan per application is typical. Stages run sequentially within a Plan but the Jobs within a stage run in parallel (across agents, if you have them). Jobs run sequentially through their Tasks. So if you want to run unit tests and integration tests at the same time, they go in separate Jobs within the same Stage. If you want deployment to happen only after all tests pass, deployment goes in a later Stage.
This is more opinionated than Jenkins' Groovy DSL. You can't express arbitrary DAGs. What you get instead is a pipeline that's easy to read and configure through the UI, which matters for teams where not everyone wants to maintain pipeline-as-code.
The Test Plan
For a Rails application, the core build plan looks like this in practice.
The first Stage is "Setup and Test." It has one or more Jobs depending on whether you've set up parallel test splitting.
The setup Task sequence within each Job:
- Checkout the source from Bitbucket (Bamboo's Bitbucket integration is a real advantage here — branch detection, PR builds, and deployment tracking work out of the box).
- Run a script task to install bundler and dependencies:
bash gem install bundler --no-document bundle install --path vendor/bundle --jobs 4 --retry 3 - Set environment variables:
RAILS_ENV=test,DATABASE_URL,REDIS_URLif you use it. - Database setup:
bash bundle exec rails db:create db:schema:load - Run RSpec with JUnit output:
bash bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
That --format RspecJunitFormatter is important. Bamboo parses JUnit XML for its test reporting — pass/fail counts, test history, flaky test detection. Without it, Bamboo sees the job as pass or fail and nothing more. With it, you get per-test history across builds, which is genuinely useful for tracking down intermittent failures.
Configure the Bamboo JUnit parser task to pick up rspec.xml and you get a full test results tab on every build.
Parallel Test Splitting
Our full RSpec suite was running in about 20 minutes on a single agent. That's too slow for a team pushing multiple times a day. The fix was parallel_tests.
Add to your Gemfile:
gem 'parallel_tests', group: :test
Then split the suite across multiple Jobs in the same Stage. Each Job gets an environment variable:
- Job 1:
TEST_FILESset to the output ofbundle exec parallel_tests --only-group 1 --num-cpus 3 spec - Job 2:
TEST_FILESset to group 2 - Job 3:
TEST_FILESset to group 3
Each Job's RSpec task becomes:
bundle exec rspec $TEST_FILES --format RspecJunitFormatter --out rspec-${BAMBOO_JOB_KEY}.xml
Using ${BAMBOO_JOB_KEY} in the output filename prevents artifact collision when Bamboo collects results from parallel jobs. Three agents running simultaneously, 20-minute suite cut to about 7 minutes.
The database setup step in each Job needs to account for parallel databases — parallel_tests handles this with rake parallel:create parallel:load_schema.
Bamboo Remote Agents
The build server itself doesn't run builds — remote agents do. We provisioned Bamboo remote agents with Ansible. Each agent is a Java process running on an Ubuntu server, registered against the Bamboo server URL with an agent token.
The Ansible role handled: installing the correct Java version, downloading the Bamboo agent JAR, configuring it as a systemd service, and setting agent capabilities. Capabilities are how Bamboo matches jobs to agents — you tag an agent with ruby.version=2.5.3 and node.version=10.x, and Bamboo only schedules jobs that require those capabilities on matching agents.
This is the right model for a team that cares about reproducible build environments. You know exactly what's on each agent because Ansible provisioned it from version-controlled configuration.
Deployment Projects
Build plans produce artifacts; deployment projects use them. This separation is one of the cleaner things about Bamboo.
A deployment project defines environments — we had PT (pre-production testing), RC (release candidate), and Production. Each environment has a sequence of deployment tasks. Ours used Capistrano: the deployment task SSHed to a deploy host and ran bundle exec cap <environment> deploy, with the built artifact and a specific git ref passed in.
Bamboo tracks which build was deployed to which environment and when. Combined with Jira integration, this means a Jira ticket can show you which deployment included a fix for it. That traceability is genuinely valuable in a regulated industry context.
Deployment to Production was manually triggered with a required approver — you promote a specific build from RC to Production, someone confirms it, Bamboo records who approved what and when.
Bugsnag Release Notifications
We used Bugsnag for error tracking. Bamboo's script tasks let you hook into Bugsnag's deploy notification API after a successful deployment:
curl -X POST "https://notify.bugsnag.com/deploy" \
-F "apiKey=${BUGSNAG_API_KEY}" \
-F "releaseStage=${DEPLOY_ENV}" \
-F "repository=${bamboo_planRepository_repositoryUrl}" \
-F "revision=${bamboo_planRepository_revision}" \
-F "appVersion=${bamboo_buildNumber}"
With this in place, Bugsnag knows about every deploy. New errors that appear after a deploy are associated with that release. If error rates spike after a deployment, you can see it immediately in Bugsnag and correlate it to the specific build. This closed a loop that previously required manual correlation between deploy times and error timestamps.
What Bamboo Gets Right
I came in skeptical of Bamboo, having worked primarily with Jenkins before. I'll give it this: for a team that already lives in Atlassian's ecosystem, the integration is real and it's good. Build status shows up in Jira. Releases link to commits link to tickets. Deployment history is visible to product managers without anyone writing custom reporting.
The tradeoff is flexibility. If you want something Bamboo doesn't support natively, you're writing a script task, which is fine but not elegant. If you want complex conditional logic in your pipeline, Bamboo's model works against you. For a straightforward Rails application with a standard test-then-deploy pipeline, it does the job and the non-engineers on the team can actually understand what's happening without reading YAML.
That's worth something.