Job
A CI/CD job is a discrete unit of work within a pipeline stage that runs on a runner or agent. Each job runs in isolation, has its own set of steps, and produces a pass or fail result that determines whether dependent jobs can proceed.
Understanding CI/CD Jobs
What Is a Job in Simple Terms
If a stage is a chapter, a job is a paragraph. Jobs are the actual executable units — the things that run commands, produce output, and succeed or fail. A Test stage might have three jobs: unit-tests, integration-tests, and coverage-check. All three can run in parallel because they do not depend on each other.
Job isolation is the key property: each job starts with a fresh environment. Environment variables set in one job do not leak to another. Files created in one job do not appear in another unless explicitly passed as artifacts. This isolation is what makes pipelines reproducible.
How It Works
+------------------------------------------+| Stage: Test || || +----------------+ +----------------+ || | Job: unit-test | | Job: lint | || | Runner: ubuntu | | Runner: ubuntu | || | Steps: | | Steps: | || | npm install | | npm install | || | npm test | | npm run lint | || | Result: PASS | | Result: PASS | || +----------------+ +----------------+ || | | || +------- both pass--+ || | |+------------------------------------------+ | v Next stage unlockedJob with artifact output (GitHub Actions):
jobs: build: runs-on: ubuntu-latest outputs: ## Expose image tag for downstream jobs image-tag: ${{ steps.tag.outputs.tag }} steps: - uses: actions/checkout@v4 - name: Generate image tag id: tag run: echo "tag=${{ github.sha }}" >> $GITHUB_OUTPUT - name: Build and push run: | docker build -t payment-api:${{ steps.tag.outputs.tag }} . docker push payment-api:${{ steps.tag.outputs.tag }} - name: Save build report uses: actions/upload-artifact@v4 with: name: build-report path: build-report.json test: needs: build runs-on: ubuntu-latest steps: - name: Download build report uses: actions/download-artifact@v4 with: name: build-report - name: Run tests against image run: | IMAGE_TAG=${{ needs.build.outputs.image-tag }} docker run payment-api:$IMAGE_TAG npm testPractical Commands
## GitHub Actions -- cancel a running jobgh run cancel RUN_ID ## GitHub Actions -- list jobs in a rungh run view RUN_ID --json jobs ## View specific job logsgh run view RUN_ID --job JOB_ID --log ## GitLab -- cancel a specific jobglab ci cancel JOB_IDTroubleshooting
| Symptom | Check | What to Look For |
|---|---|---|
| Job fails immediately | First step output | Missing dependency or wrong runner |
| Job timeout | Duration vs timeout setting | Increase timeout or fix hang |
| Jobs not running in parallel | needs dependency chain | Jobs not correctly declaring independence |
| Artifact not found in downstream job | upload/download action | Name mismatch between upload and download |
TipUse job outputs (GitHub Actions) or artifact passing to share data between jobs without polluting the environment. A downstream job should declare exactly what it needs from upstream jobs — not assume global state.
SecurityNever run untrusted code from forks in a job that has access to secrets. In GitHub Actions, pull_request_target runs in the context of the base repository and has secret access — an attacker can craft a fork PR that exfiltrates secrets if your workflow uses pull_request_target incorrectly.
Common MistakePutting all pipeline logic into a single job with 30 sequential steps. A single job cannot run steps in parallel, cannot be partially retried, and produces a single long log that is hard to read. Split independent work into separate jobs with explicit dependencies using the needs keyword.
RememberJob isolation means environment variables set in one job are not available in another job in the same workflow. Use job outputs (outputs block + GITHUB_OUTPUT) to pass values between jobs, or store values in artifacts. This isolation is intentional — it prevents jobs from accidentally depending on each other's side effects.
Frequently Asked Questions
How does a CI/CD job differ from a stage or a step in pipeline terminology?
A pipeline is organized in a hierarchy: stages are logical phases (build, test, deploy), each stage contains one or more jobs, and each job runs a sequence of steps (individual commands) inside a single runner environment. Jobs within the same stage typically run in parallel across separate runners by default, while stages usually run sequentially — this is why splitting tests into multiple parallel jobs is a standard way to cut pipeline duration.
What's a common mistake that makes CI/CD jobs slower or flakier than they need to be?
Not caching dependencies between jobs — reinstalling the same node_modules or pip packages from scratch on every single job run adds minutes of pure overhead per pipeline execution. The other frequent issue is jobs with hidden interdependencies (one job relying on state left behind by another) that only work because of execution order, which breaks the moment jobs are parallelized or a runner is swapped, since each job is supposed to run in isolation.