claudear

Production

Deploy

Run Claudear as a background service, in Docker, or in CI.

#Deployment checklist

  1. Run claudear seed so Claudear starts from now, not your entire backlog
  2. Confirm the dashboard loads at your configured port
  3. Trigger a test issue with claudear trigger to verify the full cycle
  4. Tune max_concurrent and per-source limits for your volume
  5. Enable regression monitoring and scheduled reports

#Background daemon

Start Claudear as a long-running background process:

claudear start --poll --port 3100

Flags:

  • --poll — enables polling for new issues on a timer
  • --port — sets the dashboard and webhook listen port
  • --no-webhooks — disables the webhook server
  • --no-dashboard — disables the web dashboard
  • --foreground — runs in the foreground instead of daemonizing (use it under launchd, systemd, or Docker)

Control a running daemon with:

claudear status     # check if the daemon is running
claudear pause      # pause issue processing
claudear resume     # resume issue processing
claudear stop       # stop after in-flight runs finish (up to 30s)

#Graceful shutdown

claudear start, claudear poll, claudear webhook, and claudear dry-run shut down on SIGTERM, SIGINT, or SIGHUP, as kill, systemctl stop, docker stop, Ctrl+C, or a closing terminal send them. A daemon started with claudear start also shuts down on claudear stop; stop the other commands with a signal. Any of these signals that was already ignored when Claudear started stays ignored: a Claudear started under nohup keeps running when its terminal closes, and one that a script starts in the background keeps running on Ctrl+C, because the script’s shell ignores SIGINT for it. Claudear then:

  1. Stops taking new work:
    • polling and background housekeeping start no new runs
    • issue webhooks are answered with 503 Service Unavailable and Retry-After: 60, and not recorded, so a redelivery is processed once Claudear is back (GitHub review and pull request webhooks are still accepted)
    • IPC Trigger and ProcessRetries commands are refused
  2. On a signal, interrupts the agent CLIs at once, logging Interrupting the agent CLIs of the runs in flight. Each agent CLI leads a process group of its own, out of reach of the terminal’s Ctrl+C, so Claudear passes on the SIGINT that Ctrl+C would send. It does not wait for the runs first: service managers and Docker follow SIGTERM with SIGKILL after a deadline, and agent runs take minutes. claudear stop, or a service that stops on its own, leaves the agent CLIs running instead.
  3. Waits up to 30 seconds for in-flight runs to finish, as interrupted runs do once they have recorded how they ended. A run that records a failure is retried later, like any failed run.
  4. Interrupts the agent CLIs still running after that, logging Interrupting the agent CLIs of the runs still in flight after 30s, and gives their runs up to 5 more seconds to record how they ended. Any agent process still running then is killed. Runs that background housekeeping started (retries, review runs, merge follow-ups, and release cascades) stop when the 30 seconds run out instead, and their agent CLIs are killed straight away.
  5. Exits, logging Exiting with runs still in flight if any are left. Blocking work, such as a local model call, gets up to 5 more seconds to stop before the process ends. A run that could not record how it ended leaves its fix attempt pending until a running daemon (claudear start, claudear poll or claudear webhook) finds that the run has sent no heartbeat for 5 minutes. Then an attempt started in the last 3 days goes back up for retry without spending a retry, and an older one is closed as cannot_fix, which claudear reset <source> <issue_id> undoes. Cascade attempts stay pending.

While it drains, claudear status reports Running: false. claudear stop waits up to 45 seconds for the daemon to exit and reports progress every 5 seconds. It prints Daemon stopped. and exits with code 0 once the daemon has exited, or exits with code 1 if the daemon is still running after 45 seconds. Until the daemon has exited, claudear start cannot start another one: the new daemon exits at once with Another claudear daemon is already running. Stop it first with 'claudear stop'. A signal that reaches Claudear while it shuts down, including after claudear stop, forces the exit: Claudear kills every agent CLI and dies of SIGINT at once.

Exit codes:

  • 0 — graceful shutdown, including when the drain runs out of time
  • 130 (death by SIGINT) — a signal during the shutdown forced the exit
  • 1 — a service failed or stopped on its own (The <name> service stopped unexpectedly), which started the shutdown

One-shot commands that run agents (claudear trigger, claudear action, and claudear retries process) also stop on SIGTERM, SIGINT, or SIGHUP: they interrupt their agent CLIs at once, kill any still running 5 seconds later, and give the command up to 30 seconds to record how its runs ended before it exits as interrupted, dying of SIGINT. A second signal exits at once.

Under launchd, systemd, or Docker, run claudear start --foreground so the manager keeps track of the process, and give Claudear 50 seconds to stop before it is sent SIGKILL: that covers the 30-second drain, up to 5 seconds for interrupted agent CLIs, up to 5 seconds for blocking work to stop, and a margin. The defaults fall short of that in Docker (10 seconds) and launchd (20 seconds); systemd’s 90 seconds would do. The examples below all allow 50 seconds.

#macOS (launchd)

Create a plist at ~/Library/LaunchAgents/com.claudear.daemon.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.claudear.daemon</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/claudear</string>
    <string>start</string>
    <string>--poll</string>
    <string>--port</string>
    <string>3100</string>
    <string>--foreground</string>
  </array>
  <key>WorkingDirectory</key>
  <string>/path/to/your/project</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>CLAUDEAR_GITHUB_TOKEN</key>
    <string>ghp_...</string>
    <key>CLAUDEAR_LINEAR_API_KEY</key>
    <string>lin_api_...</string>
  </dict>
  <key>StandardOutPath</key>
  <string>/tmp/claudear.out.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/claudear.err.log</string>
  <key>KeepAlive</key>
  <true/>
  <key>ExitTimeOut</key>
  <integer>50</integer>
</dict>
</plist>

Load and start the service:

launchctl load ~/Library/LaunchAgents/com.claudear.daemon.plist
launchctl start com.claudear.daemon

ExitTimeOut is how long launchd waits after SIGTERM before it sends SIGKILL. KeepAlive restarts Claudear whenever it exits, so stop it with launchctl unload ~/Library/LaunchAgents/com.claudear.daemon.plist rather than claudear stop.

#Linux (systemd)

Create a unit file at /etc/systemd/system/claudear.service:

[Unit]
Description=Claudear daemon
After=network.target

[Service]
Type=simple
WorkingDirectory=/path/to/your/project
EnvironmentFile=/etc/claudear/env
ExecStart=/usr/local/bin/claudear start --poll --port 3100 --foreground
KillMode=mixed
TimeoutStopSec=50
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Store secrets in /etc/claudear/env:

CLAUDEAR_GITHUB_TOKEN=ghp_...
CLAUDEAR_LINEAR_API_KEY=lin_api_...

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable claudear
sudo systemctl start claudear
sudo journalctl -u claudear -f   # follow logs

KillMode=mixed sends SIGTERM to Claudear only, so the agent CLIs it started get the SIGINT Claudear passes on rather than systemd’s SIGTERM, and their runs can record how they ended; systemd kills any that are left once Claudear exits. TimeoutStopSec=50 is how long systemd waits for Claudear to exit before it sends SIGKILL.

#Docker

The image runs Claudear under tini, which forwards signals to it and reaps orphaned processes. Its default command is claudear start --foreground, which serves webhooks and the dashboard on port 3100.

Compose (recommended)

If your project includes a docker-compose.yml, this is the simplest path:

docker compose up -d
docker compose logs -f app
docker compose down

Standalone

Run the container directly:

docker run -d --name claudear \
  --stop-timeout 50 \
  -v $(pwd)/claudear.toml:/app/claudear.toml:ro \
  -v claudear-data:/app/data \
  -p 3100:3100 \
  ghcr.io/abnegate/claudear:latest claudear start --foreground --poll --port 3100

Health check: GET /api/health returns 200 when Claudear is up.

Stopping the container

docker stop sends SIGTERM, which tini forwards to Claudear, so it interrupts its agent CLIs and shuts down gracefully. Docker kills the container if it is still running when the stop timeout runs out, and the default of 10 seconds is shorter than a shutdown can take: Claudear’s docker-compose.yml sets stop_grace_period: 50s, and the standalone example passes --stop-timeout 50. For a container started without either, use docker stop -t 50.

Stop the container with docker stop (or docker compose stop) rather than running claudear stop inside it: the container ends as soon as Claudear exits, and a restart policy such as unless-stopped starts it again unless Docker itself stopped it.

#Environment variables

You can override any secret in claudear.toml with an environment variable. This is the recommended approach for production so secrets stay out of config files.

Variable Overrides
CLAUDEAR_LINEAR_API_KEY issues.linear.api_key
CLAUDEAR_SENTRY_AUTH_TOKEN issues.sentry.auth_token
CLAUDEAR_GITHUB_TOKEN scm.github.token
CLAUDEAR_JIRA_API_TOKEN issues.jira.api_token
CLAUDEAR_DISCORD_BOT_TOKEN notifiers.discord.bot_token
CLAUDEAR_SLACK_BOT_TOKEN notifiers.slack.bot_token
CLAUDEAR_TLS_ENABLED tls.enabled
CLAUDEAR_TLS_DOMAINS tls.domains (comma-separated)
CLAUDEAR_TLS_EMAIL tls.email
CLAUDEAR_TLS_PRODUCTION tls.production
CLAUDEAR_GITLAB_TOKEN scm.gitlab.token
CLAUDEAR_GITLAB_WEBHOOK_SECRET scm.gitlab.webhook_secret
CLAUDEAR_JIRA_API_TOKEN issues.jira.api_token
CLAUDEAR_DISCORD_BOT_TOKEN notifiers.discord.bot_token
CLAUDEAR_SLACK_BOT_TOKEN notifiers.slack.bot_token
CLAUDEAR_EMBEDDING_MODEL Embedding model (nomic, minilm, bge)
CLAUDEAR_EMBEDDING_CACHE_DIR Embedding model cache directory
CLAUDEAR_VECTORLITE_PATH Path to vectorlite SQLite extension
CLAUDEAR_SENTRY_DSN Sentry DSN for backend error reporting
CLAUDEAR_TLS_CACHE_DIR tls.cache_dir
CLAUDEAR_TLS_HTTPS_PORT tls.https_port
CLAUDEAR_TLS_HTTP_REDIRECT_PORT tls.http_redirect_port

#Tips for production

  • Log rotation — set up log rotation or use --log-dir with daily rotation to avoid unbounded disk usage
  • Reply-capable notifications — use a reply-capable notification channel so you can answer AI questions directly from chat
  • Health monitoring — monitor the /api/health endpoint with your existing uptime checker or alerting tool
  • Concurrency — start with conservative max_concurrent (1–2) and increase as needed once you are confident in the pipeline
  • Native TLS — set tls.enabled = true with your domain in tls.domains to auto-provision Let’s Encrypt certificates without a reverse proxy. Test with tls.production = false (staging) first, then switch to production once verified
  • Security headers — the API server includes HSTS, X-Content-Type-Options, X-Frame-Options, and CSRF protection (double-submit cookie) by default. Idle sessions are automatically cleaned up
  • Local LLM — enable [llm] for offline repo classification and code chat. Set gpu_layers = 99 for GPU acceleration. The model auto-downloads on first use if model_url is configured
Claudear documentation