Diagnose Nginx, SSL, Docker, DNS, ports, and web app deployment problems from one CLI.
Project description
DeployDoctor
When your site is down at 2 a.m., DeployDoctor is the one command that tells you what is actually broken.
deploydoctor is a small, no-magic CLI that runs the checks you would otherwise run by hand when a deployment goes sideways — DNS, HTTP/HTTPS, TLS certificates, port reachability, and the usual suspects on the host itself (nginx -t, ss -tulpn, docker ps, nginx error logs).
Output is a single readable Rich report with PASS / WARN / FAIL / SKIP markers, a summary, the most likely issue, and a set of copy-pasteable next commands.
Demo
Install
pip install deploydoctor
Or with pipx (recommended for CLI tools):
pipx install deploydoctor
Development (editable install with linter + tests):
git clone https://github.com/saidamiraslanli/deploydoctor.git
cd deploydoctor
pip install -e ".[dev]"
Python 3.11+.
Quick usage
deploydoctor check example.com # full diagnosis: remote + local
deploydoctor remote example.com # external-only: DNS, HTTP, HTTPS, SSL, ports
deploydoctor local # this host: nginx, docker, ss -tulpn
deploydoctor version
deploydoctor --help
# Also runnable as a module
python -m deploydoctor check example.com
A bare hostname (example.com) or a full URL (https://example.com/foo) both work — the host is extracted automatically.
Example output
─────────── DeployDoctor Report for example.com ───────────
╭─ DNS ─────────────────────────────────────────────────────╮
│ PASS Resolved 1 A / 1 AAAA record(s) │
│ • A: 93.184.216.34 │
│ • AAAA: 2606:2800:220:1:248:1893:25c8:1946 │
╰───────────────────────────────────────────────────────────╯
╭─ HTTPS ───────────────────────────────────────────────────╮
│ FAIL 502 gateway error │
│ → Possible cause: reverse proxy cannot reach │
│ upstream app │
│ → sudo systemctl status nginx │
│ → sudo tail -n 50 /var/log/nginx/error.log │
│ → ss -tulpn │
╰───────────────────────────────────────────────────────────╯
╭─ SSL ─────────────────────────────────────────────────────╮
│ WARN Certificate expires in 12 day(s) │
│ • Subject CN: example.com │
│ • Issuer: Let's Encrypt R3 │
│ → Schedule renewal (sudo certbot renew --dry-run) │
╰───────────────────────────────────────────────────────────╯
Summary
┌────────┬──────────┬────────┬─────────┐
│ Passed │ Warnings │ Failed │ Skipped │
├────────┼──────────┼────────┼─────────┤
│ 3 │ 1 │ 1 │ 0 │
└────────┴──────────┴────────┴─────────┘
More worked examples in docs/examples/:
502-bad-gateway.md— reverse proxy can't reach upstream appssl-expired.md— certificate expired or about to expirenginx-upstream-down.md— nginx is up, the app behind it isn'tcloudflare-525.md— Cloudflare can't complete TLS handshake with origin
What problems it detects
| Layer | Detected |
|---|---|
| DNS | NXDOMAIN, missing A/AAAA, resolver timeouts |
| HTTP / HTTPS | connection refused, connect/read timeouts, too many redirects, SSL verification failures, 4xx, 5xx, 502 / 503 / 504 gateway errors, Cloudflare 525 SSL handshake failures, redirect chains |
| SSL / TLS | expired certs, certs expiring within 30 days, verify failures, missing chain, hosts without TLS |
| Ports | 80 / 443 closed, filtered, refused, or timing out from outside |
| Local host (Linux) | nginx config syntax (nginx -t), listening sockets (ss -tulpn), nginx error log tail, running Docker containers, missing tools |
Local vs remote mode
DeployDoctor exposes two scopes deliberately:
remote <domain>— outside view. Everything runs over the network: DNS, HTTP, HTTPS, SSL, port probes. Safe to run from a laptop or from a monitoring host. Does not touch the deployment host.local— inside view. Inspects the host you run it on: is nginx installed, is its config valid, what is listening, what does the error log say, what Docker containers are running. Linux-only by design; cleanlySKIPs on Windows / macOS or when commands and permissions are missing.check <domain>— runs both back-to-back, which is what you want when SSHed into the box that serves the domain.
This split exists so you can use the tool from anywhere without the local checks polluting the report with noise.
CI exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
At least one WARN |
2 |
At least one FAIL |
Friendly for cron, CI, uptime scripts:
deploydoctor remote example.com || curl -X POST https://my-alert-hook
# GitHub Actions example
- name: Post-deploy smoke check
run: deploydoctor remote example.com
Roadmap
- HTTP/2 + HTTP/3 capability probe
- TLS handshake details (protocol, cipher, OCSP stapling)
- Configurable check timeouts via CLI flag
- JSON output mode (
--json) for piping into other tools - systemd unit health checks (
is-active,is-failed) - Apache / Caddy parity for local config tests
deploydoctor watchfor continuous monitoring
Troubleshooting
nginx -treports permission denied — run withsudo; DeployDoctor will surfaceSKIPrather than fail.- All local checks
SKIP— local checks are gated to Linux. Run on the deployment host. - SSL check is
SKIP— port 443 refused; the domain likely doesn't serve TLS. - DNS
timeout— typically a firewall blocking UDP/53 on the host running the CLI.
Contributing
Pull requests welcome. See CONTRIBUTING.md for setup, lint / test commands, and the rules every new check must follow.
Quick start:
git clone https://github.com/saidamiraslanli/deploydoctor.git
cd deploydoctor
pip install -e ".[dev]"
pytest
ruff check .
License
MIT.
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 deploydoctor-0.1.2.tar.gz.
File metadata
- Download URL: deploydoctor-0.1.2.tar.gz
- Upload date:
- Size: 206.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b502f8234af71919d128b7de07468d1d6b2bb77adaf7bd61c4e83afa9fa52ed4
|
|
| MD5 |
d42508c369f5f7cf60b50f1245467808
|
|
| BLAKE2b-256 |
b8847139530ca794455d78ff0ece5eff148d2ee8cba567c66555f3677ef5c87d
|
Provenance
The following attestation bundles were made for deploydoctor-0.1.2.tar.gz:
Publisher:
workflow.yml on saidamiraslanli/deploydoctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
deploydoctor-0.1.2.tar.gz -
Subject digest:
b502f8234af71919d128b7de07468d1d6b2bb77adaf7bd61c4e83afa9fa52ed4 - Sigstore transparency entry: 1615112429
- Sigstore integration time:
-
Permalink:
saidamiraslanli/deploydoctor@c2716a8117c296f23c4dfb6c8429eb8a8a8b38bf -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/saidamiraslanli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c2716a8117c296f23c4dfb6c8429eb8a8a8b38bf -
Trigger Event:
push
-
Statement type:
File details
Details for the file deploydoctor-0.1.2-py3-none-any.whl.
File metadata
- Download URL: deploydoctor-0.1.2-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9c174a8224f61343b7653f6fcf7961d77799ba510830e7d08562243bbe1f4ea
|
|
| MD5 |
f4e2b3ef3416ce3bd6f06e8f8ef9a989
|
|
| BLAKE2b-256 |
8d8e3ea6eaf4b51eee2e70231fda7dadf12df43dc4b349d584c0c1aa38b98879
|
Provenance
The following attestation bundles were made for deploydoctor-0.1.2-py3-none-any.whl:
Publisher:
workflow.yml on saidamiraslanli/deploydoctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
deploydoctor-0.1.2-py3-none-any.whl -
Subject digest:
d9c174a8224f61343b7653f6fcf7961d77799ba510830e7d08562243bbe1f4ea - Sigstore transparency entry: 1615112435
- Sigstore integration time:
-
Permalink:
saidamiraslanli/deploydoctor@c2716a8117c296f23c4dfb6c8429eb8a8a8b38bf -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/saidamiraslanli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c2716a8117c296f23c4dfb6c8429eb8a8a8b38bf -
Trigger Event:
push
-
Statement type: