Mastering Claude Code CLI: The Complete Guide for DevOps Engineers

If you have been using Claude in a browser tab to help with code, you are leaving most of its capability on the table. Claude Code CLI brings the full power of Claude directly into your terminal — it reads your actual codebase, runs real commands, edits files, commits code, and integrates with every tool in your DevOps stack. This guide covers everything from installation to advanced patterns that most engineers never discover.


What Is Claude Code?

Claude Code is an agentic coding assistant that lives in your terminal. Unlike a chat interface where you paste code snippets, Claude Code:

  • Reads your entire codebase — it understands the real structure of your project
  • Runs actual commandsgit, docker, kubectl, terraform, npm, whatever you need
  • Edits files directly — makes changes across multiple files in a single operation
  • Integrates with your IDE — VS Code and JetBrains show diffs in their native viewers
  • Connects to external tools — databases, GitHub, AWS, Slack, and more via MCP

For DevOps engineers specifically, this means you can say “review our Terraform configs for security issues, fix what you find, and open a PR” — and it will actually do it.


Installation

macOS / Linux:

curl -fsSL https://claude.ai/install.sh | bash

Homebrew:

brew install --cask claude-code

Windows (PowerShell):

irm https://claude.ai/install.ps1 | iex

After installing, run claude in any project directory to start a session. Run claude --help to see all options.

Quick start:

cd my-project
claude                        # interactive session
claude -p "explain this project structure"   # one-off query
claude --continue             # resume last session

CLAUDE.md — Your Project’s Permanent Memory

Every time you start a Claude Code session, it reads CLAUDE.md files automatically. Think of it as a briefing document — you write it once and never have to re-explain your project setup.

Where to put them

FileScopeShared via Git?
~/.claude/CLAUDE.mdAll your projectsNo — personal preferences
./CLAUDE.mdThis projectYes — team-wide conventions
./.claude/CLAUDE.mdThis project (alt)Yes
./CLAUDE.local.mdThis projectNo — gitignored overrides

What to put in a project CLAUDE.md

The golden rule: include what Claude cannot infer from reading the code. Commands, gotchas, team conventions, and architecture decisions that are not obvious from the files.

# My Infrastructure Platform

## Build and Test
- Build: `npm run build`
- Tests: `npm test` — run single tests with `npm test -- --grep "auth"`
- Lint: `npm run lint`
- Type check: `npm run typecheck`

## Architecture
- API handlers in `src/api/handlers/`
- Terraform configs in `infra/`
- Each service has its own `Dockerfile.prod`

## Code Style
- ES modules only (import/export) — no CommonJS
- 2-space indentation
- camelCase for variables, PascalCase for types

## Git Workflow
- Feature branches: `feature/description`
- Always create a PR — never push directly to main

## Common Commands
- Deploy staging: `npm run deploy:staging`
- Deploy prod: `npm run deploy:prod`
- View production logs: `npm run logs:prod`
- Run migrations: `npm run db:migrate`

## Secrets
- Never hardcode credentials — all secrets in AWS Secrets Manager
- Use `.env.example` for documentation, never `.env` itself

## Known Gotchas
- The auth service requires `NODE_ENV=production` to initialise properly
- Database migrations run automatically on deploy — no manual step needed
- Some integration tests require a local Redis instance on port 6379

Path-scoped rules

For large projects, create .claude/rules/ with topic-specific files that only load when Claude is working in the relevant area:

.claude/
├── CLAUDE.md          ← always loaded
└── rules/
    ├── api.md          ← loads when editing src/api/**
    ├── security.md     ← loads when editing any auth-related file
    └── infra.md        ← loads when editing infra/**

Example .claude/rules/security.md:

---
paths:
  - "src/api/**/*.ts"
  - "infra/**/*.tf"
---

# Security Rules

- Always validate and sanitise user input at API boundaries
- Use parameterised queries — never string interpolation in SQL
- Never log secrets, tokens, or PII — scrub before logging
- Return generic error messages externally — no stack traces
- Require authentication on all non-public endpoints

Keep CLAUDE.md under 200 lines. Longer files reduce adherence — Claude loses track of rules buried at line 350.


Saving Tokens — How to Keep Costs Low

Context is the most important resource to manage. Every message you send includes your entire conversation history, all the files Claude has read, and every command output. It adds up fast.

Check your usage

/cost      # estimated session cost
/usage     # detailed token breakdown
/context   # see what is consuming context space

Clear between tasks

The single most effective thing you can do:

/clear

When you finish a task and start something unrelated, clear the context. Stale conversation history about your auth bug wastes tokens on every message when you switch to asking about your CI pipeline.

Compact your context

/compact focus on infrastructure changes

/compact summarises the conversation history but lets you tell it what to preserve. Use it when a session gets long but you are not ready to clear completely.

Add a compact instruction to your CLAUDE.md:

## Compact instructions
When compacting, always preserve:
- Code changes made so far
- Test results and error messages
- Architecture decisions reached

Choose the right model for the task

/model claude-sonnet-4-6    # default — best balance of cost and capability
/model claude-opus-4-6      # for complex architectural decisions
/model claude-haiku-4-5     # for simple repetitive tasks

Sonnet handles 95% of DevOps tasks well. Save Opus for genuinely complex problems.

Control extended thinking

Extended thinking is powerful but expensive. Turn it down for simple tasks:

/effort low     # disable extended thinking
/effort high    # enable for complex problems
/effort xhigh   # max thinking tokens

Use subagents for investigation

When you need Claude to explore a large codebase to answer a question, spawn a subagent instead of doing it in your main session:

Use a subagent to investigate why our authentication service is slow

Subagents run in their own context and report back a summary — keeping your main conversation clean.


MCP — Connecting Claude to Everything

MCP (Model Context Protocol) is what lets Claude Code talk to external systems — databases, GitHub, AWS, Slack, your custom APIs. Once configured, Claude can query them directly without you having to copy-paste data.

Adding MCP servers

# Add a GitHub MCP server
claude mcp add --transport http github https://api.github.com/mcp/ \
  --header "Authorization: Bearer YOUR_GITHUB_PAT"

# Add a local Postgres server
claude mcp add --transport stdio postgres \
  "mcp-postgres --connection-string postgresql://user:pass@localhost/db"

Or configure directly in .claude/settings.json:

{
  "mcp": {
    "servers": {
      "github": {
        "transport": "http",
        "url": "https://api.github.com/mcp/",
        "headers": {
          "Authorization": "Bearer YOUR_GITHUB_PAT"
        }
      },
      "postgres": {
        "transport": "stdio",
        "command": "mcp-postgres",
        "args": ["--connection-string", "postgresql://user:pass@localhost/db"]
      }
    }
  }
}

Useful MCP servers for DevOps

ServerWhat it enables
GitHubSearch PRs, read comments, create issues, review code diffs
Postgres / MySQLQuery databases directly, analyse schemas, debug data issues
AWSList resources, check CloudWatch logs, manage S3
Web FetchPull in documentation, error pages, API references
FilesystemRead files outside your project root
SlackPost notifications, read channel history
MemoryPersist information across sessions
DockerInspect containers, images, network config

Check what is loaded

/mcp    # list configured servers and their context cost

Disable servers you are not using — each one adds to your context overhead.


Hooks — Automating the Boring Parts

Hooks are shell commands that run automatically at specific points in Claude’s lifecycle. Unlike CLAUDE.md instructions (which are advisory), hooks are deterministic — they always run.

Hook events

EventWhen it fires
SessionStartAt the beginning of every session
PreToolUseBefore Claude runs any tool
PostToolUseAfter a tool succeeds
PermissionRequestWhen Claude needs your approval
SessionEndWhen the session closes

Configuration

Hooks go in .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/format-on-save.sh"
          }
        ]
      }
    ]
  }
}

Practical hook examples

Auto-format files after Claude edits them:

#!/bin/bash
# ~/.claude/hooks/format-on-save.sh
input=$(cat)
files=$(echo "$input" | jq -r '.tool_output.files_edited[]' 2>/dev/null)

for file in $files; do
  case "$file" in
    *.ts|*.js) npx prettier --write "$file" 2>/dev/null ;;
    *.py)      black "$file" 2>/dev/null ;;
    *.go)      gofmt -w "$file" 2>/dev/null ;;
    *.tf)      terraform fmt "$file" 2>/dev/null ;;
  esac
done

exit 0

Block destructive commands:

#!/bin/bash
# ~/.claude/hooks/block-destructive.sh
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

BLOCKED="rm -rf|kubectl delete|terraform destroy|DROP TABLE|truncate"

if echo "$cmd" | grep -qE "$BLOCKED"; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook. Review and run manually."
    }
  }'
else
  exit 0
fi

Load environment variables at session start:

{
  "env": {
    "NODE_ENV": "development",
    "DEBUG": "app:*",
    "POSTGRES_HOST": "localhost",
    "AWS_REGION": "us-east-1"
  }
}

Or use a SessionStart hook to load from a .envrc:

#!/bin/bash
# ~/.claude/hooks/load-env.sh
[ -f .envrc ] && source .envrc
exit 0

Permissions — Fewer Prompts, More Flow

By default, Claude asks for permission before running commands or editing files. Here is how to reduce that friction without giving up safety.

Permission modes

Press Shift+Tab to cycle through modes:

ModeBehaviour
DefaultAsk before every tool use
Accept EditsAuto-approve file edits, ask for shell commands
PlanShow plan first, then ask before executing
AutoSmart classifier — only asks for genuinely risky commands

Or set a default in .claude/settings.json:

{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}

Allowlist common operations

Stop being asked about things you always approve:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(docker build *)",
      "Bash(docker ps)",
      "Bash(kubectl get *)",
      "Bash(terraform plan *)",
      "Bash(terraform init)",
      "Bash(terraform fmt *)",
      "Edit",
      "WebFetch(domain:github.com)",
      "WebFetch(domain:docs.aws.amazon.com)"
    ],
    "deny": [
      "Bash(terraform apply)",
      "Bash(terraform destroy *)",
      "Bash(kubectl delete *)",
      "Bash(rm -rf *)",
      "Read(.env)"
    ]
  }
}

Separate team and personal settings

Put shared permissions in .claude/settings.json (committed to git) and personal overrides in .claude/settings.local.json (gitignored):

// .claude/settings.local.json (your personal overrides)
{
  "permissions": {
    "allow": [
      "Bash(brew *)",
      "Bash(nvim *)"
    ]
  }
}

Essential Slash Commands

CommandWhat it does
/clearReset conversation history — do this between tasks
/compact [focus]Summarise history, optionally preserving specific areas
/costShow estimated session cost
/usageDetailed token usage breakdown
/contextShow what is taking up context space
/model [name]Switch Claude model mid-session
/effort [low|high|xhigh]Control extended thinking
/initGenerate a starter CLAUDE.md for the current project
/mcpManage and check MCP servers
/memoryView and edit CLAUDE.md and auto memory
/hooksView configured hooks
/rename [name]Name the current session for easy resumption
/resume [name]Resume a named session
/rewindRestore to a previous message (double-tap Esc also works)
/btw [question]Ask a quick side question that never enters history
/reviewRun a code review subagent
/doctorDiagnose installation issues

IDE Integration

VS Code

Install the Claude Code extension from the marketplace, or:

code --install-extension anthropic.claude-code

Useful keyboard shortcuts:

ActionShortcut
Focus Claude inputCmd+Esc
Insert file referenceOption+K
New conversationCmd+N
Open in new tabCmd+Shift+Esc

When Claude proposes file changes, VS Code shows them in its native diff viewer — you can approve, reject, or edit each change individually before accepting.

JetBrains (IntelliJ, PyCharm, GoLand, WebStorm)

Install Claude Code from Settings → Plugins → Marketplace.

Connect your terminal session to the IDE:

claude
/ide

This links the terminal session to your open IDE so diffs appear there instead of in the terminal.


DevOps-Specific Workflows

Terraform

# Review configs for issues
claude "review infra/main.tf for security issues and best practices"

# Plan changes safely
claude "run terraform plan and explain what would change"

# Generate module documentation
claude "generate README documentation for our terraform modules"

Restrict permissions so Claude can plan but never apply:

{
  "permissions": {
    "allow": ["Bash(terraform plan *)", "Bash(terraform init)", "Bash(terraform fmt *)"],
    "deny": ["Bash(terraform apply)", "Bash(terraform destroy *)"]
  }
}

Kubernetes

# Review manifests
claude "review all manifests in k8s/ for security and best practices"

# Generate a Helm chart
claude "create a Helm chart for our Node.js API service"

# Debug a failing deployment
cat deployment-error.log | claude -p "what is causing this failure and how do I fix it?"

CI/CD with GitHub Actions

name: Claude Code Review
on: [pull_request]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: AI Code Review
        run: |
          claude -p "Review the changes in this PR for bugs, security issues, and best practices. Be specific." \
            --output-format json > review.json          
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Pipe data directly

# Debug log output
cat app.log | claude -p "what errors are here and what is causing them?"

# Review recent git history
git log -20 --oneline | claude -p "summarise what changed recently"

# Analyse docker logs
docker logs myapp --tail 100 | claude -p "what is wrong with this service?"

Tips Most Engineers Miss

1. Ask side questions without polluting context:

/btw what Node version does this project need?

The answer appears in an overlay and is never saved to history.

2. Pipe directly from the shell:

cat errors.log | claude -p "what is causing this?"

3. Use ! for quick shell commands inside a session:

> ! git log --oneline -5
> ! docker ps

4. Name your sessions:

claude -n "oauth-implementation"
# Later
claude --resume oauth-implementation

5. Use worktrees for parallel work:

# Two independent sessions, two branches
claude -w feature-auth     # terminal 1
claude -w bugfix-api       # terminal 2

6. Set a cost limit:

claude -p "audit entire infrastructure" --max-budget-usd 2.00

7. Use --add-dir in monorepos:

claude --add-dir ../shared-lib ../api-service -p "refactor the shared auth code"

8. Double-tap Escape to rewind without typing /rewind.

9. Generate your CLAUDE.md automatically:

/init

Claude reads your project and generates a starter CLAUDE.md. Edit it from there.

10. Check what the auto-memory system saved:

/memory

Claude saves learnings across sessions automatically. This shows what it remembers about you and your project.


Here is a practical starting point for a DevOps project:

.claude/settings.json (commit to git):

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(npm run *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(terraform plan *)",
      "Bash(terraform init)",
      "Bash(terraform fmt *)",
      "Bash(docker build *)",
      "Bash(docker ps)",
      "Bash(kubectl get *)"
    ],
    "deny": [
      "Bash(terraform destroy *)",
      "Bash(terraform apply)",
      "Bash(kubectl delete *)",
      "Bash(rm -rf *)",
      "Read(.env)"
    ]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [{ "type": "command", "command": ".claude/hooks/format.sh" }]
      }
    ]
  }
}

CLAUDE.md (commit to git):

# Project Name

## Build and Test
- Build: `npm run build`
- Test: `npm test`
- Lint: `npm run lint`

## Deployment
- Staging: `npm run deploy:staging`
- Production requires manual approval — create a PR

## Architecture
- API: `src/api/` — Express + TypeScript
- Infra: `infra/` — Terraform on AWS
- K8s manifests: `k8s/`

## Rules
- No secrets in code — use AWS Secrets Manager
- All infra changes via Terraform — no manual console changes
- Every feature needs a PR and passing tests

Run /init to let Claude generate a starter version based on your actual project, then edit it.


Claude Code is at its best when it has context about your project, constraints on what it can touch, and clear instructions about your conventions. Invest 30 minutes in your CLAUDE.md and settings once, and every session after that starts with a Claude that already understands your project.

The full documentation is at docs.anthropic.com/claude-code.

Abhay

Abhay Pratap Singh

DevOps Engineer passionate about automation, cloud infrastructure, and self-hosted tools. I write about Kubernetes, Terraform, DNS, and everything in between.