Skip to content
CI/CD Best Practices

CircleCI

Caching

steps:
  - restore_cache:
      keys:
        # when lock file changes, use increasingly general patterns to restore cache
        - gradle-repo-v1-{{ .Branch }}-{{ checksum "dependencies.lockfile" }}
        - gradle-repo-v1-{{ .Branch }}-
        - gradle-repo-v1-
  - save_cache:
      paths:
        - ~/.gradle/caches
        - ~/.gradle/wrapper
      key: gradle-repo-v1-{{ .Branch }}-{{ checksum "dependencies.lockfile" }}

Debugging Pipelines

Debugging Tools for CircleCI

CircleCI provides powerful debugging features including SSH access and insights dashboards to help diagnose failures.

1. SSH Rerun for Interactive Debugging

When to use: Need to inspect the CI environment interactively, investigate why a step fails, or debug environment-specific configuration issues.

How to enable:

  1. Go to your failed job in CircleCI dashboard
  2. Click “Rerun” dropdown in top right
  3. Select “Rerun job with SSH”
  4. Wait for job to start
  5. Copy SSH command from job output:
    SSH enabled. To connect:
    ssh -p PORT USERNAME@HOST

What you get:

  • Full SSH access to the running container
  • All environment variables and configuration
  • Ability to run failed commands manually
  • Inspect files and directory structure

Usage tips:

# Navigate to working directory
cd ~/project

# Check environment
printenv | grep -i api
env | sort

# Re-run failed command with modifications
npm test -- --verbose

# Check tool versions
node --version
npm --version

# Inspect files
ls -la
cat .circleci/config.yml

Important notes:

  • SSH session times out after 10 minutes of inactivity (configurable up to 2 hours)
  • Connection stays open for duration even if steps complete
  • Type exit or close terminal when done to free resources

2. Insights Dashboard

When to use: Identify patterns in failures, find flaky tests, track pipeline performance over time, or spot bottlenecks.

How to access:

  1. Go to CircleCI dashboard
  2. Click “Insights” in left sidebar
  3. Select your project

What you see:

  • Success rate over time: Spot trends in pipeline reliability
  • Duration trends: See if builds are getting slower
  • Flaky test detection: Tests that pass/fail intermittently
  • Most failed tests: Focus debugging efforts on problematic tests
  • Credit usage: Track resource consumption

Key metrics to monitor:

  • Pipeline success rate (target: >95%)
  • Mean time to recovery (MTTR)
  • Test flakiness percentage
  • Job duration trends (catch performance regressions)

3. Step Output and Timing

When to use: Identify which specific step is failing or taking too long, understand the execution flow, or optimize slow pipelines.

How to access: Built into every job view - each step is expandable with timing information.

What you get:

  • Timing for each step (helps identify bottlenecks)
  • Full stdout/stderr output
  • Color-coded output (errors in red)
  • Collapsible sections for readability

Debugging tips:

# Add timing for custom commands
- run:
    name: "Run tests with timing"
    command: |
      echo "Starting tests at $(date)"
      time npm test
      echo "Finished tests at $(date)"

# Add debug output
- run:
    name: "Debug environment"
    command: |
      echo "=== Environment Variables ==="
      env | sort
      echo "=== Installed tools ==="
      node --version
      npm --version
      echo "=== Disk space ==="
      df -h

Store and access logs as artifacts:

- store_artifacts:
    path: test-results
    destination: test-results

- store_artifacts:
    path: /tmp/logs
    destination: logs

Access artifacts via:

  • Job page → Artifacts tab
  • Or direct URL: https://output.circle-artifacts.com/output/job/:job-id/artifacts/:container-index/:path

Local Validation

Local Validation with CircleCI CLI

CircleCI’s official CLI lets you validate config syntax and run jobs locally—perfect for catching errors before push.

Installation

curl -fLSs https://circle.ci/cli | bash

Verify it works:

circleci version

Essential Commands

Validate config syntax:

circleci config validate

# Validate specific file
circleci config validate -c .circleci/config.yml

This catches 80% of config errors instantly without needing to push or run anything.

Run job locally:

# Run entire pipeline (if supported)
circleci local execute

# Run specific job
circleci local execute --job build
circleci local execute --job test

Pass environment variables:

circleci local execute --job build \
  --env NODE_ENV=development \
  --env API_URL=http://localhost:3000 \
  --env DATABASE_URL=postgres://localhost/test

Process config (expand orbs):

circleci config process .circleci/config.yml

This shows you what your config looks like after orbs are expanded—helpful for debugging orb usage.

Important Limitations

CircleCI’s local executor has significant constraints:

Only machine executor works locally:

# ✅ Works locally
jobs:
  build:
    machine:
      image: ubuntu-2004:current
    steps:
      - checkout
      - run: npm test

# ❌ Won't run locally (but syntax validates)
jobs:
  build:
    docker:
      - image: node:18
    steps:
      - checkout
      - run: npm test

Other limitations:

  • ❌ No workflow execution (only individual jobs)
  • ❌ Orbs don’t execute locally (but expand with config process)
  • ❌ Can’t access CircleCI contexts or project environment variables
  • ❌ No workspace persistence between jobs
  • ❌ Remote Docker (setup_remote_docker) doesn’t work

What to Use It For

Best use cases:

Config syntax validation (most valuable)

# Before every commit with config changes
circleci config validate

Testing job script logic

  • If using machine executor, full local execution
  • Validates bash scripts, commands, and step ordering

Quick verification before push

  • Catches typos, missing keys, invalid YAML
  • Prevents “fix config” commits

Debugging orb expansion

circleci config process .circleci/config.yml > expanded-config.yml

See what your config actually does after orbs expand.

Skip it for:

  • Jobs using docker or windows executors
  • Testing workflows with dependencies
  • Validating orb behavior
  • Full pipeline integration testing

Workflow Integration

Pre-commit validation:

Create .git/hooks/pre-commit:

#!/bin/bash
if git diff --cached --name-only | grep -q "^.circleci/"; then
  echo "Validating CircleCI config..."
  circleci config validate
  if [ $? -ne 0 ]; then
    echo "❌ CircleCI config validation failed"
    exit 1
  fi
  echo "✅ Config valid"
fi

Make it executable:

chmod +x .git/hooks/pre-commit

Now config is validated automatically on every commit that touches CircleCI files.

Common Issues

“Error: Unable to parse YAML”

  • Check for YAML syntax errors (indentation, missing colons)
  • Run through a YAML validator
  • Common issue: tabs instead of spaces

“Error: job … not found”

  • Ensure job name matches exactly (case-sensitive)
  • Check for typos in job name
  • Verify job is defined in config

“Docker not running”

  • For local execute, Docker must be running
  • Check with: docker ps
  • Start Docker Desktop if needed

“Permission denied”

  • CLI might not have execute permissions
  • Fix: chmod +x $(which circleci)
  • Or reinstall with proper permissions

Tips

  • Always validate before push: Make it a habit—circleci config validate takes 1 second
  • Use for syntax only if not on machine executor: Config validation is still valuable even if you can’t run locally
  • Expand orbs to debug: circleci config process reveals what orbs actually do
  • Combine with Docker Compose: For Docker-based jobs, use Docker Compose for local testing (see generic approach)
  • Check CLI updates: Run circleci update periodically for latest features

Parallelisation

To run a set of concurrent jobs, you will need to add a workflows section to your existing .circleci/config.yml file.

The simple example below shows the default workflow orchestration with two concurrent jobs. The workflows key needs to have a unique name. In this example, the unique name is build_and_test. The jobs key is nested under the uniquely named workflow, and contains the list of job names. Since the jobs have no dependencies, they will run concurrently.

version: 2.1

jobs:
  build:
    docker:
      - image: cimg/<language>:<version TAG>
    steps:
      - checkout
      - run: <command>
  test:
    docker:
      - image: cimg/<language>:<version TAG>
    steps:
      - checkout
      - run: <command>
workflows:
  build_and_test:
    jobs:
      - build
      - test

Workload Identity