CircleCI Orbs are just Docker images with a specific directory structure and a orb.yml file, which makes them surprisingly flexible and easier to build than you might think.

Let’s say we want to build an orb that simplifies deploying a Node.js application to AWS Elastic Beanstalk. First, we need a config.yml file for our orb. This file defines the commands and jobs that our orb will expose.

Here’s a basic config.yml for our aws-elastic-beanstalk orb:

version: 2.1

description: |
  Deploys Node.js applications to AWS Elastic Beanstalk.

commands:
  deploy:
    description: "Deploy application to Elastic Beanstalk"
    parameters:
      app_name:
        type: string
        default: "my-app"
        description: "Name of the Elastic Beanstalk application."
      env_name:
        type: string
        default: "my-app-env"
        description: "Name of the Elastic Beanstalk environment."
      version_label:
        type: string
        default: ""
        description: "A label for the application version. Defaults to git commit SHA."
      region:
        type: string
        default: "us-east-1"
        description: "AWS region for Elastic Beanstalk."
      aws_access_key_id:
        type: string
        description: "AWS Access Key ID."
      aws_secret_access_key:
        type: string
        description: "AWS Secret Access Key."
    steps:
      - checkout
      - run:
          name: "Install EB CLI"
          command: |
            sudo apt-get update -y
            sudo apt-get install -y python3-pip python3-dev build-essential libssl-dev libffi-dev
            pip3 install awsebcli --upgrade --user
            echo 'export PATH=$PATH:~/.local/bin' >> $BASH_ENV
            source $BASH_ENV
      - run:
          name: "Configure AWS Credentials"
          command: |
            aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
            aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
            aws configure set region $REGION
      - run:
          name: "Create Elastic Beanstalk Application Version"
          command: |
            EB_VERSION_LABEL=${VERSION_LABEL:-$(git rev-parse --short HEAD)}
            eb init --platform Node.js --region << parameters.region >> << parameters.app_name >>
            eb create << parameters.env_name >> --instance_type t2.micro --sample-application
            eb deploy --label $EB_VERSION_LABEL --message "Deploying version $EB_VERSION_LABEL"
          environment:
            AWS_ACCESS_KEY_ID: << parameters.aws_access_key_id >>
            AWS_SECRET_ACCESS_KEY: << parameters.aws_secret_access_key >>
            REGION: << parameters.region >>

jobs:
  build_and_deploy:
    docker:
      - image: cimg/node:16.13.0
    steps:
      - deploy:
          app_name: "my-node-app"
          env_name: "my-node-app-env"
          region: "us-west-2"
          aws_access_key_id: "$AWS_ACCESS_KEY_ID"
          aws_secret_access_key: "$AWS_SECRET_ACCESS_KEY"

workflows:
  version: 2.1
  build_and_deploy_workflow:
    jobs:
      - build_and_deploy

This config.yml defines a single command, deploy, which takes several parameters for configuring the Elastic Beanstalk deployment. It also defines a job, build_and_deploy, that uses a Node.js Docker image and calls our deploy command.

Now, to make this an orb, we need to package it. Orbs are distributed as Docker images. You’ll need a Dockerfile to build this image.

Here’s a simple Dockerfile:

FROM circleci/circleci-base:latest

COPY . /work/
RUN circleci-agent orb pack /work > /orb/orb.yml

This Dockerfile starts from the circleci-base image, copies your orb’s project files (including config.yml) into the image, and then uses the circleci-agent orb pack command to create the orb.yml file. This command essentially takes your config.yml and any other referenced files and bundles them into a single orb.yml manifest.

To build and publish this orb, you’d typically use the CircleCI CLI. First, make sure you have the CLI installed. Then, navigate to your orb’s project directory.

You’ll need to create a CircleCI project for your orb. In the CircleCI UI, create a new project and point it to your Git repository. This project will be used to build and version your orb.

The core of building an orb lies in the circleci-agent orb pack command. When you run this locally or in a CI job, it reads your config.yml and any other referenced files, resolves dependencies, and generates a single orb.yml file. This orb.yml is the actual artifact that gets published.

Here’s how you’d build the Docker image:

docker build -t your-dockerhub-username/my-aws-eb-orb:v0.1.0 .

And then push it to Docker Hub:

docker push your-dockerhub-username/my-aws-eb-orb:v0.1.0

Once your orb’s Docker image is pushed to a registry (like Docker Hub), you can publish it to the CircleCI Orb Registry. You’ll use the CircleCI CLI for this:

First, log in to the CircleCI CLI:

circleci setup

Then, publish your orb:

circleci orb publish your-dockerhub-username/my-aws-eb-orb:v0.1.0 your-org/my-aws-eb-orb@0.1.0

The your-org/my-aws-eb-orb@0.1.0 part is the canonical name for your orb. The first part (your-org) is your CircleCI organization name, followed by the orb name and its version.

After publishing, you can use your orb in other CircleCI configurations like this:

version: 2.1

orbs:
  aws-eb: your-org/my-aws-eb-orb@0.1.0

jobs:
  deploy-app:
    docker:
      - image: cimg/node:16.13.0
    steps:
      - aws-eb/deploy:
          app_name: "my-production-app"
          env_name: "my-production-env"
          region: "us-east-1"
          aws_access_key_id: "$AWS_ACCESS_KEY_ID"
          aws_secret_access_key: "$AWS_SECRET_ACCESS_KEY"

workflows:
  version: 2.1
  deploy_production:
    jobs:
      - deploy-app

The circleci-agent orb pack command, when run within a CircleCI environment for an orb project, automatically knows to look for config.yml and package it correctly. When you’re developing locally, you can use circleci orb pack . to generate the orb.yml file in the current directory, which is useful for testing.

The real magic of orbs is that they’re versioned. When you publish an orb, you give it a version tag (e.g., 1.0.0). This allows users to pin their workflows to specific versions, ensuring stability. You can also use semantic versioning ranges (e.g., 1.x, 1.0.x) for more flexibility, but pinning to a specific version is generally recommended for production deployments to avoid unexpected changes.

The circleci-agent orb pack command is the linchpin. It takes your orb’s source files, resolves any internal dependencies or references, and produces a single, self-contained orb.yml manifest. This manifest is what gets uploaded to the CircleCI Orb Registry.

The next step in mastering orbs is understanding how to manage complex dependencies and parameters within them, and how to integrate them with different CI/CD strategies.

Want structured learning?

Take the full Circleci course →