Introduction
Nozie is a lightweight Python monitoring probe that watches your Docker containers, detects runtime errors, and reports them to the Nozie backend — where Nozie analyzes root causes and opens an auto-fix GitHub pull request.
The library runs entirely in background daemon threads and never blocks your application. No AI keys or GitHub tokens are required on your server — all intelligence happens in the Nozie cloud backend.
Requirements
- Python
≥ 3.8 - Docker daemon running on the same host
- A Nozie API key — get one from your dashboard
- Your GitHub repo connected in the dashboard (for auto-fix PRs)
How It Works
From container crash to a merged GitHub pull request — Nozie closes the loop automatically while you sleep.
ERROR, Exception, Traceback, or FATAL, it captures the buffer as context for analysis.Thread Architecture
Nozie spawns a ThreadPoolExecutor with up to 5 worker threads:
- nozie-main — orchestrates the agent startup and keeps the process alive
- stats loop — sends heartbeat stats every
stats_intervalseconds - events loop — watches Docker events for instant status changes
- log monitor (per container) — one thread per monitored container
stop() for clean shutdown.
Installation
Install the Nozie library from PyPI using pip. It requires Python 3.8 or newer and an active Docker daemon on the same host.
Install via pip
pip install nozie
Dependencies
Nozie automatically installs these packages as dependencies:
| Package | Version | Purpose |
|---|---|---|
docker | ≥ 7.0.0 | Docker SDK for Python — connects to the Docker socket |
pydantic | ≥ 2.0.0 | Configuration validation and type safety |
requests | ≥ 2.31.0 | HTTP client for reporting to the Nozie backend |
Optional extras
If you are self-hosting the Nozie backend and want to use
the built-in AI analysis or GitHub integration on your own server, install the backend
extras:
pip install nozie[backend]
This adds two optional packages not required by the probe itself:
| Package | Purpose |
|---|---|
anthropic ≥ 0.25.0 | Anthropic Python SDK for AI-powered error analysis |
PyGithub ≥ 2.0.0 | GitHub API client for opening auto-fix pull requests |
pip install nozie is all you need to run the monitoring
probe on your server. The [backend] extra is only needed when you run
the Nozie analysis server itself.
Verify installation
import nozie print("nozie ready")
Docker requirement
Nozie connects to Docker via docker.from_env(), which reads
the DOCKER_HOST environment variable or falls back to the
local socket at /var/run/docker.sock.
/var/run/docker.sock by default.Windows: Docker Desktop exposes the named pipe
npipe:////./pipe/docker_engine.
The Python Docker SDK detects this automatically.
Quickstart
Two lines of code are all you need to start monitoring. Get your API key from the dashboard, then add the agent to your project.
Minimal setup
Start monitoring all running containers:
import time from nozie import NozieAgent NozieAgent(api_key="nz_k_your_key_here").start() # .start() is non-blocking — keep the process alive, or the daemon # threads it spawned die with it the instant the script reaches the end. while True: time.sleep(1)
agent_runner.py) and run
it as a separate service in your docker-compose.yml, with
/var/run/docker.sock mounted into that service only — not the
app's own container, as shown below.
# docker-compose.yml services: your-app: # ... your existing app service, unchanged ... nozie-agent: image: python:3.11-slim volumes: - /var/run/docker.sock:/var/run/docker.sock - ./agent_runner.py:/agent_runner.py working_dir: / command: sh -c "pip install nozie && python agent_runner.py" environment: - NOZIE_API_KEY=${NOZIE_API_KEY} depends_on: - your-app
Monitor a specific container
from nozie import NozieAgent agent = NozieAgent( api_key="nz_k_your_key_here", container="my-production-app", ) agent.start()
Using environment variables
All NozieConfig fields support the NOZIE_ prefix. You can skip passing values directly:
# .env
NOZIE_API_KEY=nz_k_your_key_here
NOZIE_CONTAINER=my-production-app
NOZIE_STATS_INTERVAL=60
NOZIE_ERROR_COOLDOWN=120
import os from dotenv import load_dotenv from nozie import NozieAgent load_dotenv() # NozieConfig reads NOZIE_* env vars automatically NozieAgent(api_key=os.environ["NOZIE_API_KEY"]).start()
Custom backend URL
If you are self-hosting the Nozie backend, specify dashboard_url:
from nozie import NozieAgent NozieAgent( api_key="nz_k_your_key_here", dashboard_url="https://your-backend.example.com", ).start()
.start() is non-blocking and returns immediately.
Place it at the top of your application startup and let it run in the background.
NozieAgent
The main entry point. Instantiate it with your API key, call .start(),
and the agent begins monitoring in the background.
Constructor
api_key: str,
container: Optional[str] = None,
**kwargs,
)
All keyword arguments are passed through to NozieConfig. See the Configuration page for the full list.
| Parameter | Type | Required | Description |
|---|---|---|---|
api_key |
str |
Yes | Your Nozie API key. Starts with nz_k_. Get it from your dashboard. |
container |
Optional[str] |
No | Container name or ID to monitor. If None, all running containers are monitored. |
**kwargs |
— | No | Additional config: dashboard_url, stats_interval, error_cooldown. |
Methods
.start()
Starts background monitoring. Spawns a daemon thread named nozie-main
which in turn launches the stats loop, events loop, and one log monitor thread per container.
Non-blocking — returns immediately.
agent = NozieAgent(api_key="nz_k_...") agent.start() # returns immediately, agent runs in background
.stop()
Signals the agent to stop gracefully. Sets the internal stop event and shuts down the thread pool. Because threads are daemon threads, this is optional — the process exit will clean them up automatically.
.restart()
self,
container_name: str,
timeout: int = 10,
) -> dict
Restarts a container by name and reports the event to the Nozie backend.
Reloads the container metadata first, then issues container.restart(timeout=timeout).
Returns a result dict with the following keys:
| Key | Type | Description |
|---|---|---|
container_id | str | Short container ID (first 12 chars) |
container_name | str | Container name as reported by Docker |
restarted_at | str | ISO 8601 UTC timestamp of the restart |
success | bool | True if the restart call succeeded |
error | Optional[str] | Error message string if the restart failed, otherwise None |
result = agent.restart("my-production-app", timeout=15) if result["success"]: print(f"Restarted at {result['restarted_at']}") else: print(f"Failed: {result['error']}")
POST /api/v1/agent/restart so the dashboard can log and display the restart history.
Configuration
NozieConfig is a Pydantic model that validates all configuration
fields. It also reads values automatically from environment variables prefixed with
NOZIE_.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Nozie Product API Key. Obtain from your dashboard under Settings → API Keys. |
container |
Optional[str] |
None |
Specific container name or ID. If None, all running and stopped containers are monitored. |
dashboard_url |
str |
"https://nozie.xyz" |
Nozie backend base URL. Override for self-hosted deployments. |
stats_interval |
int |
30 |
Seconds between heartbeat stats reports. Lower values give more granular data at the cost of more network traffic. |
error_cooldown |
int |
60 |
Minimum seconds between error reports per container. Prevents flooding the backend when logs are spamming errors. |
Environment variables
Because NozieConfig uses env_prefix = "NOZIE_",
every field can be set via environment variable. The variable name is the field name uppercased,
prefixed with NOZIE_.
| Variable | Corresponds to |
|---|---|
NOZIE_API_KEY | api_key |
NOZIE_CONTAINER | container |
NOZIE_DASHBOARD_URL | dashboard_url |
NOZIE_STATS_INTERVAL | stats_interval |
NOZIE_ERROR_COOLDOWN | error_cooldown |
Example
from nozie import NozieAgent agent = NozieAgent( api_key="nz_k_your_key_here", container="my-production-app", stats_interval=60, # heartbeat every 60s error_cooldown=120, # max one error report per 2 min dashboard_url="https://your-backend.example.com", ) agent.start()
Docker Monitoring
Nozie uses the Docker SDK to watch containers, collect stats, and stream logs.
Internally this is handled by DockerClientWrapper.
Container targeting
On startup, get_target_containers() is called to find containers to watch:
- If
containeris set — fetches the single named container - If
containerisNone— lists all containers (running and stopped)
If no containers are found at startup, the agent enters a retry loop, polling for new containers
every stats_interval seconds and attaching log monitors as they appear.
Container stats
Every stats_interval seconds, the following metrics are collected per container:
| Metric | Type | Source |
|---|---|---|
container_id | str | First 12 chars of Docker container ID |
container_name | str | Docker container name |
status | str | running, exited, paused, etc. |
cpu_percent | float | Calculated from CPU delta / system delta × num_cores × 100 |
memory_usage_bytes | int | memory_stats.usage from Docker stats API |
memory_limit_bytes | int | memory_stats.limit from Docker stats API |
memory_percent | float | usage / limit × 100 |
restarts | int | container.attrs["RestartCount"] |
started_at | str | ISO timestamp from container.attrs["State"]["StartedAt"] |
running,
all numeric metrics are returned as 0. The status field is still reported correctly.
Docker events
In parallel with the stats loop, Nozie watches the Docker event stream filtered to
type=container. The following actions trigger an immediate
stats report without waiting for the next cycle:
| Event | Reported status |
|---|---|
die, stop, kill, oom | exited |
pause | paused |
start, unpause, restart | running |
restarting / running /
paused / exited), and Nozie reports it that way
(0.3.0+) instead of conflating a deliberate pause with a crash.
Log streaming
stream_logs(container) is a generator that yields decoded log lines.
It streams with tail=100 (last 100 lines on attach) and continues
following new lines indefinitely. On timeout or Docker API errors it reconnects automatically
after a short sleep.
Error Detection
Nozie scans every log line against a compiled regex pattern. When a match is found, it captures a rolling buffer of recent log lines as context and reports to the backend.
Detection pattern
The agent uses the following case-insensitive pattern:
(ERROR|Exception|Traceback|FATAL)
This catches the most common failure signatures across Python, Java, Node.js, Go, and generic system logs.
Self-log filter
Lines emitted by the Nozie probe itself are prefixed with [nozie]
and are automatically excluded from pattern matching. This prevents the agent's own diagnostic
output from triggering false-positive error reports.
Rolling log buffer
Each container maintains its own 50-line rolling buffer. Every incoming log line is
appended to this buffer. When an error is detected, the full buffer content is
joined and sent as the log_snippet. This gives the AI
backend rich context about what happened just before the crash.
# Internal buffer logic (simplified) log_buffer = [] for line in stream_logs(container): log_buffer.append(line) if len(log_buffer) > 50: log_buffer.pop(0) # drop oldest line if error_pattern.search(line): snippet = "\n".join(log_buffer) report_error(container.name, snippet)
Cooldown mechanism
To prevent flooding the backend when an app crashes in a loop, Nozie enforces a
per-container cooldown. After an error report is sent, no further reports are made
for that container until error_cooldown seconds have passed
(default: 60 seconds).
| Scenario | Behavior |
|---|---|
First error in container app-A | Report sent immediately |
Second error in app-A within 60s | Silently skipped |
Error in app-B (different container) | Report sent independently |
Error in app-A after 60s | Report sent again |
error_cooldown in your config if your app produces bursts of
correlated errors. Setting it to 300 (5 min) works well for apps
that crash-loop before a restart policy kicks in.
API Payloads
The agent communicates with the Nozie backend over two REST endpoints. All requests use Bearer token authentication via the API key.
Authentication
Every request includes:
Authorization: Bearer <api_key> Content-Type: application/json
Stats endpoint
POST /api/v1/agent/stats
Payload:
{
"agent_version": "0.2.1",
"containers": [
{
"container_id": "a1b2c3d4e5f6",
"container_name": "my-production-app",
"status": "running",
"cpu_percent": 12.5,
"memory_usage_bytes":134217728,
"memory_limit_bytes":536870912,
"memory_percent": 25.0,
"restarts": 0,
"started_at": "2026-03-17T00:00:00Z"
}
]
}
Error endpoint
POST /api/v1/agent/error
Payload:
{
"agent_version": "0.2.0",
"container_name": "my-production-app",
"log_snippet": "...last 50 log lines as a single string..."
}
Restart endpoint
POST /api/v1/agent/restart
Called automatically by agent.restart() after a successful container restart. Payload:
{
"agent_version": "0.2.1",
"container_name": "my-production-app",
"container_id": "a1b2c3d4e5f6",
"restarted_at": "2026-04-11T12:34:56.789Z",
"success": true
}
The backend stores this in the container_restarts table and the dashboard displays the last restart time per container in the monitoring view.
Error handling
The NozieReporter._post() method handles HTTP failures silently — it logs a warning and returns False without raising. The agent continues running even if the backend is unreachable. Retries happen automatically on the next report cycle.
Architecture
Nozie is split into four focused modules. The agent is the only public entry point — the rest are internal implementation details.
Modules
| Module | Class | Responsibility |
|---|---|---|
nozie.agent |
NozieAgent |
Public entry point. Orchestrates threads, error pattern matching, cooldown tracking. |
nozie.config |
NozieConfig |
Pydantic model. Validates all config fields and reads NOZIE_* env vars. |
nozie.docker_client |
DockerClientWrapper |
Wraps the Docker SDK. Handles container listing, stats collection, event watching, log streaming. |
nozie.reporter |
NozieReporter |
Thin HTTP client. POSTs stats, error, and restart payloads to the backend. Never raises — logs warnings instead. |
Data flow
NozieAgent.start()
│
├─► nozie-main thread
│ │
│ ├─► stats_loop ──────────────────────► NozieReporter.report_stats()
│ │ (every N sec) │
│ │ └─► POST /api/v1/agent/stats
│ │
│ ├─► events_loop ──────────────────────► NozieReporter.report_stats()
│ │ (Docker events) (status change payloads)
│ │
│ └─► log_monitor_loop (per container)
│ │
│ ├─► rolling 50-line buffer
│ ├─► regex match (ERROR|Exception|...) [case-insensitive]
│ ├─► skip lines starting with [nozie] [self-filter]
│ └─► cooldown check
│ │
│ └─► NozieReporter.report_error()
│ │
│ └─► POST /api/v1/agent/error
│
NozieAgent.restart(container_name)
│
├─► DockerClientWrapper.restart_container()
│ └─► container.reload() + container.restart(timeout)
│
└─► NozieReporter.report_restart()
│
└─► POST /api/v1/agent/restart
Backend responsibilities
Everything that happens after the POST is handled by the Nozie backend. The library itself has no knowledge of:
- Nozie analysis or the Anthropic API
- GitHub PR creation or the GitHub API
- Repository code reading or patching
- User accounts, dashboard, or billing
This separation keeps the probe lean and removes the need for any secrets on your server.