Where JSON Appears in DevOps
| Stage | JSON Usage | Examples |
|---|---|---|
| Source | Config files | package.json, tsconfig.json, .eslintrc.json |
| CI Pipeline | Matrix strategies, outputs | GitHub Actions fromJson(), step outputs |
| Build | Build manifests, lockfiles | package-lock.json, build-manifest.json |
| Infrastructure | Infrastructure as Code | Terraform .tf.json, CloudFormation |
| Deployment | Container configs | Docker labels, Kubernetes ConfigMaps |
| Monitoring | Structured logging | JSON log lines, error reports |
1. GitHub Actions & JSON
Dynamic Matrix from JSON
.github/workflows/test.ymlyaml
1name: Test Matrix2on: push34jobs:5 setup:6 runs-on: ubuntu-latest7 outputs:8 matrix: ${{ steps.set-matrix.outputs.matrix }}9 steps:10 - uses: actions/checkout@v411 - id: set-matrix12 run: |13 MATRIX=$(cat .github/test-matrix.json)14 echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"1516 test:17 needs: setup18 strategy:19 matrix: ${{ fromJson(needs.setup.outputs.matrix) }}20 runs-on: ${{ matrix.os }}21 steps:22 - uses: actions/checkout@v423 - uses: actions/setup-node@v424 with:25 node-version: ${{ matrix.node }}26 - run: npm ci && npm test.github/test-matrix.jsonjson
1{2 "os": ["ubuntu-latest", "windows-latest", "macos-latest"],3 "node": ["18", "20", "22"],4 "include": [5 { "os": "ubuntu-latest", "node": "22", "coverage": true }6 ],7 "exclude": [8 { "os": "windows-latest", "node": "18" }9 ]10}Passing JSON Between Steps
Step outputs with JSONyaml
1steps:2 - name: Get deployment info3 id: deploy-info4 run: |5 INFO=$(curl -s https://api.example.com/deployments/latest)6 echo "info=$(echo $INFO | jq -c .)" >> "$GITHUB_OUTPUT"78 - name: Use deployment info9 run: |10 VERSION=$(echo '${{ steps.deploy-info.outputs.info }}' | jq -r '.version')11 echo "Deploying version: $VERSION"Validating JSON in CI
JSON validation stepyaml
1- name: Validate JSON config files2 run: |3 for file in config/*.json; do4 echo "Validating $file..."5 python -m json.tool "$file" > /dev/null || exit 16 done7 echo "All JSON files are valid"2. Terraform & JSON
Terraform supports JSON as an alternative to HCL. Files ending in .tf.json are parsed as JSON:
main.tf.json — Terraform in JSON syntaxjson
1{2 "terraform": {3 "required_providers": {4 "aws": {5 "source": "hashicorp/aws",6 "version": "~> 5.0"7 }8 }9 },10 "provider": {11 "aws": {12 "region": "us-east-1"13 }14 },15 "resource": {16 "aws_s3_bucket": {17 "data_bucket": {18 "bucket": "my-app-data-2026",19 "tags": {20 "Environment": "production",21 "ManagedBy": "terraform"22 }23 }24 }25 }26}Terraform State (terraform.tfstate)
Terraform stores infrastructure state as JSON. Understanding this format helps with debugging and importing:
terraform.tfstate (partial)json
1{2 "version": 4,3 "terraform_version": "1.9.0",4 "resources": [5 {6 "mode": "managed",7 "type": "aws_s3_bucket",8 "name": "data_bucket",9 "instances": [10 {11 "attributes": {12 "id": "my-app-data-2026",13 "bucket": "my-app-data-2026",14 "region": "us-east-1",15 "arn": "arn:aws:s3:::my-app-data-2026"16 }17 }18 ]19 }20 ]21}Warning
Never edit
terraform.tfstate manually. It contains sensitive data (resource IDs, connection strings) and must stay consistent with real infrastructure. Use terraform state commands instead.Using jq with Terraform
Query Terraform outputsbash
1# Get Terraform outputs as JSON2terraform output -json | jq '.api_url.value'34# List all resource IDs in state5terraform show -json | jq '.values.root_module.resources[].values.id'67# Extract specific resource attributes8terraform show -json | jq '.values.root_module.resources[] | select(.type == "aws_s3_bucket") | .values.bucket'3. Docker & JSON
Docker Inspect Output
Inspecting containers with jqbash
1# Get container IP address2docker inspect my-container | jq '.[0].NetworkSettings.IPAddress'34# Get all environment variables5docker inspect my-container | jq '.[0].Config.Env'67# Get port mappings8docker inspect my-container | jq '.[0].NetworkSettings.Ports'910# Get image labels11docker inspect my-image | jq '.[0].Config.Labels'Container Labels as JSON Metadata
Dockerfile labelstext
1LABEL maintainer="[email protected]"2LABEL version="2.1.0"3LABEL description="API service"4LABEL org.opencontainers.image.source="https://github.com/org/repo"5LABEL deploy.config='{"replicas": 3, "memory": "512Mi", "cpu": "250m"}'4. AWS CloudFormation (JSON Templates)
CloudFormation template (partial)json
1{2 "AWSTemplateFormatVersion": "2010-09-09",3 "Description": "API Lambda function",4 "Parameters": {5 "Environment": {6 "Type": "String",7 "AllowedValues": ["dev", "staging", "production"],8 "Default": "dev"9 }10 },11 "Resources": {12 "ApiFunction": {13 "Type": "AWS::Lambda::Function",14 "Properties": {15 "FunctionName": { "Fn::Sub": "api-${Environment}" },16 "Runtime": "nodejs20.x",17 "Handler": "index.handler",18 "MemorySize": 256,19 "Timeout": 30,20 "Environment": {21 "Variables": {22 "NODE_ENV": { "Ref": "Environment" }23 }24 }25 }26 }27 }28}5. Structured JSON Logging
Production applications should emit structured JSON logs for machine parsing:
Structured log line (JSON Lines format)json
1{"timestamp":"2026-04-02T14:30:00Z","level":"info","message":"Request handled","method":"GET","path":"/api/users","status":200,"duration_ms":45,"request_id":"req_abc123"}2{"timestamp":"2026-04-02T14:30:01Z","level":"error","message":"Database query failed","error":"connection timeout","query":"SELECT * FROM users","duration_ms":5000,"request_id":"req_def456"}Analyzing JSON logs with jqbash
1# Find all errors2cat app.log | jq 'select(.level == "error")'34# Slow requests (>1 second)5cat app.log | jq 'select(.duration_ms > 1000) | {path, duration_ms, timestamp}'67# Count requests by status code8cat app.log | jq -s 'group_by(.status) | map({status: .[0].status, count: length})'910# Average response time11cat app.log | jq -s '[.[].duration_ms] | add / length'Best Practices
- ✓Validate all JSON config files in CI before deployment
- ✓Use
jq -c(compact) when passing JSON in environment variables or step outputs - ✓Store dynamic configs as JSON files in the repo, not hardcoded in pipeline YAML
- ✓Use structured JSON logging in production for queryable, machine-parseable logs
- ✓Pin Terraform state to remote backends, never commit
tfstateto git - ✗Don't store secrets in JSON config files — use environment variables or secret managers
- ✗Don't embed unescaped JSON in shell strings — use files or base64 encoding
Try It — Validate a CI/CD JSON Config
Try It Yourself
A GitHub Actions matrix config — validate before using
Try These Tools
Continue Learning
Frequently Asked Questions
Where does JSON appear in CI/CD pipelines?
JSON is everywhere in CI/CD: GitHub Actions matrix strategies, Terraform state files and variable definitions, Docker container labels, AWS CloudFormation templates, Azure ARM templates, npm/yarn lockfiles, build tool outputs, and API calls within pipeline scripts.
Should I use JSON or YAML for GitHub Actions?
GitHub Actions workflow files must be YAML (.yml). However, JSON appears inside workflows: matrix strategies, output parsing with fromJson(), and API calls with jq. You can also define reusable JSON configs that workflows read at runtime.
What is Terraform JSON syntax?
Terraform supports both HCL (its native syntax) and JSON for configuration files. JSON Terraform files use .tf.json extension and follow a specific structure where block types become JSON object keys. JSON is useful for generated configs and API-driven infrastructure management.
How do I pass JSON between CI/CD steps?
In GitHub Actions, use outputs with toJson() and fromJson(). For shell-based pipelines, write JSON to a file and read it in the next step. Use jq to extract specific values. For complex data, base64-encode the JSON to avoid shell escaping issues.
How do I validate JSON in a CI/CD pipeline?
Add a validation step: use python -m json.tool, jq ., or npx jsonlint-cli to validate JSON files. For schema validation, use ajv-cli. Fail the pipeline if validation fails to prevent broken configs from reaching production.