Read-only diagnostic CLI for Ubuntu systems, with explained findings and stable JSON output.
Project description
ubuntu-doctor
Read-only diagnostic CLI for Ubuntu systems, with explained findings and stable JSON output.
Problem
When an Ubuntu machine slows down or misbehaves, the first diagnosis is always the same
sequence of manual commands: df -h, free -h, systemctl --failed, ss -tulpn,
scanning /var/log, checking for broken packages. It is repetitive, easy to miss a step,
and the results end up scattered across several outputs with no interpretation.
ubuntu-doctor runs those checks for you and returns a prioritized, explained report:
what is critical, what is a warning, what is healthy — each finding with a suggested next
step. It is read-only by design and never changes the system.
Features
- Disk usage per mount point (snap/pseudo filesystems excluded)
- Memory and swap, judged by available memory
- CPU load average and the heaviest processes (by name, no command lines)
- Failed
systemdunits - Listening TCP/UDP ports, with owning process where visible
- Oversized log files under
/var/log - Broken packages and pending (security) updates
- Colored text, stable JSON (a documented contract), and sanitized Markdown output
- Severity-based exit codes and graceful degradation without privileges
Architecture
See docs/architecture.md. The rule is: collectors don't decide,
rules don't collect, renderers don't calculate, and the domain core never touches the OS.
flowchart LR
CLI[cli.py] --> ORCH[orchestrator]
ORCH --> COL[collectors]
COL --> RULES[rules]
RULES --> FIND[Finding / Report]
FIND --> REND[render: text / json / markdown]
Requirements
- Ubuntu 22.04 LTS or newer (Debian-family)
- Python 3.11+
Installation
Once published, the recommended install is with pipx:
pipx install ubuntu-doctor
From a clone, using uv:
uv sync
uv run ubuntu-doctor --help
Usage
ubuntu-doctor full-check # run every check and aggregate
ubuntu-doctor disk --json # a single area, machine-readable
ubuntu-doctor cpu --top 10 # ten heaviest processes
ubuntu-doctor export-report -o report.md
Example full-check output (hostname anonymized):
CRITICAL Failed unit: virtualbox.service [failed]
LSB: VirtualBox Linux kernel module
→ Inspect each failed unit with: systemctl status <unit> and
journalctl -u <unit> -b. ubuntu-doctor never restarts services for you.
UNKNOWN Broken packages
Could not determine broken packages: dpkg: ... Permission denied
→ Re-run with sufficient privilege to check: sudo dpkg --audit.
OK Disk usage: / [14.6%]
14.6% used (741.7 GB free of 914.8 GB)
OK Memory usage [36.9%]
36.9% used (14.7 GB available of 23.3 GB); swap 0.0% of 19.7 GB
OK CPU load [3.41]
load average 1.96 / 3.41 / 3.66 on 8 core(s)
OK Listening ports [26]
26 listening ports (17 tcp, 9 udp).
Overall: CRITICAL (myhost)
Commands
| Command | Purpose |
|---|---|
full-check |
Run every check and aggregate |
disk, memory, cpu, services, network, logs, packages |
Run a single area |
export-report |
Write a sanitized Markdown report |
version |
Print the version |
Common options: --json, --verbose, --config PATH. cpu also takes --top N;
export-report takes --output/-o PATH and --force.
Exit codes: 0 no problem · 1 at least one CRITICAL · 2 usage/config error ·
3 internal error.
Configuration
Thresholds resolve from three layers, each overriding the previous: defaults → TOML file → environment.
TOML file (via --config PATH, or ~/.config/ubuntu-doctor/config.toml if it exists):
[thresholds]
disk_warn_percent = 80
disk_crit_percent = 90
mem_warn_percent = 85
mem_crit_percent = 95
log_warn_bytes = 104857600 # 100 MB
log_crit_bytes = 1073741824 # 1 GB
Environment variables override the file:
| Variable | Overrides |
|---|---|
UBUNTU_DOCTOR_DISK_WARN / UBUNTU_DOCTOR_DISK_CRIT |
Disk usage % |
UBUNTU_DOCTOR_MEM_WARN / UBUNTU_DOCTOR_MEM_CRIT |
Memory usage % |
UBUNTU_DOCTOR_LOG_WARN / UBUNTU_DOCTOR_LOG_CRIT |
Log size (bytes) |
There are deliberately no --threshold-* flags: the file and environment already cover
per-threshold overrides without cluttering every command.
The JSON contract
--json emits a stable object with a schema_version (currently 1). Consumers can rely
on the shape; any breaking change bumps schema_version and the package's major version.
Testing
uv run pytest # 149 tests, 100% coverage, fully offline
uv run ruff check .
uv run mypy
Every test runs offline and deterministically. Command-based checks are tested against
real output captured once into tests/fixtures/.
Design decisions
Recorded as ADRs in docs/adr/: pure collectors for testability, psutil
over /proc parsing, read-only by design, and threshold/UNKNOWN semantics.
Troubleshooting
- "Could not determine broken packages: ... Permission denied" —
dpkg --auditneeds the dpkg lock, which requires privilege. This is expected: the check degrades toUNKNOWNrather than failing. Runsudo dpkg --audityourself to check. - Listening ports show no process —
sscannot name processes owned by other users without privilege. The port is still listed; only the owner is hidden. command not foundfor a check — on a minimal systemsystemctl,ssoraptmay be absent; that check reportsUNKNOWNand the rest still run.
Limitations
- Ubuntu/Debian only.
- Read-only: it diagnoses, it never fixes.
- No history and no continuous monitoring in v1 (each run is a snapshot).
- File-permission auditing is deliberately omitted (too prone to false positives).
- Behavior inside containers can differ (cgroup-reported limits).
Roadmap
- v1.1: Docker container check; snap packaging.
- v1.2: optional history in SQLite with run-to-run comparison.
- v2.0: extract the collection layer into a reusable
linuxfactslibrary.
Contributing
Issues and PRs welcome. Please keep the tool read-only, keep tests offline, and run
ruff, mypy and pytest before opening a PR. See the issue and PR templates.
License
MIT — see LICENSE.
Contact
David Oliveira · davidoliveira.devbr@gmail.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ubuntu_doctor-1.0.0.tar.gz.
File metadata
- Download URL: ubuntu_doctor-1.0.0.tar.gz
- Upload date:
- Size: 100.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cddca0f8931f4657372e903785780caf0b0f55716a0f589124c6d6d1787958b1
|
|
| MD5 |
d5d8364e04ccfcde7a64676c73bd03c9
|
|
| BLAKE2b-256 |
f6fc62dfc130751304d5ae2548a1eed509ad3919054517b4baf6e6067b412519
|
Provenance
The following attestation bundles were made for ubuntu_doctor-1.0.0.tar.gz:
Publisher:
release.yml on david-oliveira-dev/ubuntu-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ubuntu_doctor-1.0.0.tar.gz -
Subject digest:
cddca0f8931f4657372e903785780caf0b0f55716a0f589124c6d6d1787958b1 - Sigstore transparency entry: 2275458052
- Sigstore integration time:
-
Permalink:
david-oliveira-dev/ubuntu-doctor@f3db148e34d1b0fe8c5bc26efd25853b5415f4f0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/david-oliveira-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f3db148e34d1b0fe8c5bc26efd25853b5415f4f0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ubuntu_doctor-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ubuntu_doctor-1.0.0-py3-none-any.whl
- Upload date:
- Size: 32.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4268e1e4db72e0421bf4e3a261310c992acbcaad6e840c519bfb75f56c57e7b
|
|
| MD5 |
3226a28ec7c61f5160638e0202b4f0d2
|
|
| BLAKE2b-256 |
88fde36a2837528bea53d4c2e5a1ef5f3eab868837321b2188801b6ba7083997
|
Provenance
The following attestation bundles were made for ubuntu_doctor-1.0.0-py3-none-any.whl:
Publisher:
release.yml on david-oliveira-dev/ubuntu-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ubuntu_doctor-1.0.0-py3-none-any.whl -
Subject digest:
a4268e1e4db72e0421bf4e3a261310c992acbcaad6e840c519bfb75f56c57e7b - Sigstore transparency entry: 2275458481
- Sigstore integration time:
-
Permalink:
david-oliveira-dev/ubuntu-doctor@f3db148e34d1b0fe8c5bc26efd25853b5415f4f0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/david-oliveira-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f3db148e34d1b0fe8c5bc26efd25853b5415f4f0 -
Trigger Event:
push
-
Statement type: