How to Run Claude Code in Headless Mode with -p Flag
I wanted to run Claude Code as a scheduled task to automatically review my server logs every night. I set up a cron job and walked away, expecting to find a summary in the morning. Instead, I found my server with a stuck process waiting for input that would never come.
The Problem: Interactive Mode Blocks Automation
Here’s what I tried first:
# This hangs forever in cron0 3 * * * claude "Review the logs in /var/log/app.log and alert on errors"The cron job ran at 3 AM, but Claude Code started in interactive mode, waiting for me to confirm permissions or provide input. Since cron has no terminal, the process just sat there until I manually killed it hours later.
I needed a way to run Claude Code without any interactive prompts, something that could execute a task and exit cleanly.
The Solution: The -p Flag
The -p flag enables headless mode in Claude Code. It tells Claude to execute the task and exit without any interactive UI or waiting for user input.
claude -p "Review the logs in /var/log/app.log and summarize any errors"But here’s the thing almost nobody explains properly: the -p flag alone isn’t enough for safe automation. You need to combine it with permission controls.
Understanding Permission Controls
When Claude runs in headless mode, it can’t ask you for permission to use tools. If it tries to read a file or run a command that requires permission, the task will fail.
I learned this the hard way:
claude -p "Review all Python files and suggest improvements"# Result: Fails when Claude tries to read files without permissionThe solution is to pre-approve specific tools using --allowedTools:
claude -p "Review all Python files and suggest improvements" \ --allowedTools 'Read' 'Bash(git diff)' 'Bash(python *)'This pattern allows Claude to read files, run git diff, and execute Python commands without asking for permission.
Building a Safe Cron Job
After several iterations, I arrived at a robust setup for automated tasks:
0 3 * * * cd /app && claude -p "Review logs for errors" \ --allowedTools 'Read' 'Bash(curl *)' \ --max-turns 10 \ --max-budget-usd 0.50Let me break down each component:
-p: Enables headless mode - no UI, no waiting for input
--allowedTools 'Read' 'Bash(curl *)': Pre-approves specific tools. The wildcard pattern Bash(curl *) allows any curl command.
--max-turns 10: Limits the conversation to 10 turns. Without this, Claude could get stuck in a loop.
--max-budget-usd 0.50: Caps the API cost at 50 cents. Essential for automation where you can’t monitor costs in real-time.
cd /app: Ensures Claude works in the correct directory. Without this, Claude might operate in the wrong location.
Piping Input for Context
One powerful pattern is piping content directly into Claude:
cat /var/log/app.log | claude -p "Explain these errors and suggest fixes" \ --allowedTools 'Read' \ --max-turns 5This sends the log file content as context to Claude, which can then analyze it without needing file system access. It’s more secure because Claude doesn’t need permission to read files.
CI/CD Integration
The same pattern works in CI/CD pipelines. Here’s a GitHub Actions example:
- name: Claude Code Review run: | claude -p "Review changed files for security issues" \ --allowedTools 'Read' 'Bash(git diff)' \ --max-turns 15 \ --max-budget-usd 1.00The key is balancing autonomy with safety. You want Claude to do useful work, but you need guardrails.
Continuing from Previous Sessions
Another useful feature is combining -p with -c to continue from a previous session:
claude -c -p "Based on our previous discussion, implement the changes we discussed"This is useful for multi-step workflows where you want Claude to build on previous context.
Common Mistakes to Avoid
I made most of these mistakes, so you don’t have to:
-
No permission controls: Without
--allowedTools, Claude will fail when it needs to use tools that require permission. -
No budget limits: I once ran a long analysis task overnight and woke up to a $15 bill. Now I always set
--max-budget-usd. -
No turn limits: Without
--max-turns, Claude can get stuck in loops or make excessive API calls. -
Wrong working directory: I spent hours debugging why Claude couldn’t find my files. The issue was a missing
cdcommand. -
Expecting interactive features: Confirmation prompts, progress bars, and other UI elements don’t exist in headless mode. Design your prompts accordingly.
How It Actually Works
Under the hood, the -p flag changes Claude’s behavior in three ways:
-
No tool confirmation prompts: Claude proceeds with pre-approved tools automatically.
-
Immediate exit: After completing the task (or hitting a limit), Claude exits with a status code. Zero means success.
-
Stdout output: Results go to stdout, making it easy to capture or redirect the output.
claude -p "Analyze code quality" --allowedTools 'Read' > analysis.txtWhen to Use Headless Mode
Headless mode shines in these scenarios:
- Scheduled maintenance: Nightly security scans, log analysis, report generation
- CI/CD pipelines: Automated code reviews, deployment checks, test analysis
- Scripted workflows: Chaining multiple Claude tasks together
- Batch processing: Running the same task across multiple files or projects
It’s not ideal for exploratory work where you want to iterate with Claude interactively.
Summary
The -p flag transforms Claude Code from an interactive assistant into a headless automation tool. The key is combining it with proper safety controls:
claude -p "your task" \ --allowedTools 'Read' 'Bash(specific-command *)' \ --max-turns N \ --max-budget-usd XAlways set --allowedTools, --max-turns, and --max-budget-usd when running in automated environments. This ensures Claude can complete useful work without unintended actions or runaway costs.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments