Localsetup localsetup-npm-management
Manage Nginx Proxy Manager (NPM) reverse proxy hosts via its REST API using the native Python client npm_api.py. Use when creating, modifying, diagnosing, removing, or cleaning up proxy hosts, or when coordinating Docker service deployments with NPM routing.
git clone https://github.com/CruxExperts/localsetup
T=$(mktemp -d) && git clone --depth=1 https://github.com/CruxExperts/localsetup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/_localsetup/skills/localsetup-npm-management" ~/.claude/skills/cruxexperts-localsetup-localsetup-npm-management && rm -rf "$T"
_localsetup/skills/localsetup-npm-management/SKILL.mdNginx Proxy Manager (NPM) management
Purpose
Give an AI agent the ability to manage reverse proxy configuration in Nginx Proxy Manager from the terminal. Covers create, modify, diagnose, remove, cleanup, and backup/restore workflows for proxy hosts, with coordinated Docker operations.
When to use
- User asks to expose a new service via NPM (create proxy host).
- User asks to update, disable, enable, or reconfigure an existing proxy host.
- User asks to troubleshoot a 502, SSL error, WebSocket failure, or unreachable service.
- User asks to remove or decommission a service.
- User asks to prune orphan proxy hosts or unused Docker resources.
- User asks to backup or restore NPM configuration.
Do not use for NPM access lists, TCP/UDP streams, or redirection hosts (extend as needed).
Tooling (framework standard)
The framework default is Python 3.10+. This skill uses
npm_api.py, a native Python client that talks directly to the NPM REST API using only Python standard library (urllib, json, configparser). No Bash, no curl, no jq, no third-party packages required.
npm_api.py handles:
- Config loading from
with hostile-input sanitization.npm-api.conf - Token fetch and cache (auto-refreshes when less than 1 hour remains).
- All proxy host operations (list, search, show, create, update, enable, disable, delete).
- Backup of proxy hosts, users, settings, access lists, and certificates.
- Structured GFM output for agent and human consumption.
- Actionable STDERR errors with source context per INPUT_HARDENING_STANDARD.md.
Prerequisites
- Python 3.10+ on the target machine (no other dependencies).
- Docker and Docker Compose running.
- NPM deployed in a container; admin API reachable (default:
).http://127.0.0.1:81 - A shared Docker network (e.g.
) that NPM and all public-facing containers are attached to.npm_default - NPM admin credentials.
Directory layout
<TOOLS_DIR>/npm-api/ npm_api.py # Native Python client (primary tool) npm-api.conf # Local config (gitignored, chmod 600) npm-api.conf.example # Template (safe to commit) data/ # Token cache and backup directory (auto-created)
Default
<TOOLS_DIR>: ~/.localsetup/tools. Adapt to environment.
Config file (npm-api.conf)
NGINX_IP=127.0.0.1 NGINX_PORT=81 API_USER=admin@example.com API_PASS=yourpassword DATA_DIR=<TOOLS_DIR>/npm-api/data # optional; defaults to data/ next to script
Set permissions:
chmod 600 <TOOLS_DIR>/npm-api/npm-api.conf. Gitignore this file.
npm_api.py loads the config, fetches a bearer token, and caches it under data/<IP_PORT>/token/. Token refresh is automatic.
Architecture rule
Every container that needs public HTTP/HTTPS access:
- Is attached to
(default:<NPM_NETWORK>
).npm_default - Does NOT publish host ports 80 or 443.
- Has an NPM proxy host pointing to it by Docker container name (not IP).
NPM listens on 80/443 and forwards by hostname. Docker DNS resolves container names within the shared network.
Removing a service = delete NPM proxy host + stop/remove containers. Creating a service = deploy container on
<NPM_NETWORK> + create NPM proxy host.
Proxy host default template
| Parameter | Default |
|---|---|
| forward_scheme | |
| ssl_forced | |
| http2_support | |
| hsts_enabled | |
| caching_enabled | |
| block_exploits | |
| allow_websocket_upgrade | |
| access_list_id | (none) |
| advanced_config | (empty) |
Use
forward_scheme: https only when the container's internal port actually speaks TLS. Enable allow_websocket_upgrade only for apps that use WebSockets. Use advanced_config only for nginx-level tuning (e.g. client_max_body_size 1000M).
Key commands
All commands are run as
python3 npm_api.py <flag> [args]. No interactive prompts are ever shown.
| Action | Command |
|---|---|
| Check connectivity / token | |
| List all proxy hosts | |
| Search by domain | |
| Show one host | |
| Create proxy host | |
| Update a field | |
| Enable | |
| Disable | |
| Delete | |
| Backup | |
Optional flags for
--host-create:
| Flag | Effect |
|---|---|
| Use HTTPS to reach backend (default: http) |
| Enable WebSocket upgrade |
| Do not redirect HTTP to HTTPS |
| Disable HTTP/2 |
| Enable HSTS header |
| Enable NPM caching |
| Disable exploit blocking |
| Apply NPM access list N |
| Raw nginx config block |
Proxy host IDs are integers assigned by NPM. Always fetch a fresh list before modify, delete, or enable/disable. Do not reuse IDs from memory.
Workflows
Create (deploy service + expose via NPM)
- Define the stack: attach public-facing containers to
. Do not publish ports 80 or 443.<NPM_NETWORK> - Deploy:
docker compose up -d - For each public container, gather: domain_names, forward_host (container name), forward_port, forward_scheme, WebSocket need.
- Create proxy host:
. Prompt user to attach an SSL certificate.python3 npm_api.py --host-create <domain> -i <container> -p <port> [options] - Optional: create the matching DNS record using
.localsetup-cloudflare-dns - Verify:
and test the public URL.python3 npm_api.py --host-search <domain>
Modify (destructive, double confirmation required)
Safety gates:
- User states intent.
- Agent shows: proxy host ID(s), domain(s), container name(s), and a concise summary of what will change. Waits.
- User must confirm with a phrase containing the word "modify" (e.g. "confirm modify"). Vague replies are not accepted.
Steps: identify current state (
python3 npm_api.py --host-list and docker inspect), show changes, wait for confirmation, apply Docker-side changes, then update NPM proxy host if needed:
python3 npm_api.py --host-update <id> forward_host=<new_name> python3 npm_api.py --host-update <id> forward_port=<new_port> python3 npm_api.py --host-update <id> forward_scheme=https
Diagnose (inspect and troubleshoot)
- Get overview:
,docker ps -a
,docker network inspect <NPM_NETWORK>
,python3 npm_api.py --host-list
.python3 npm_api.py --info - Per service: check container logs, network membership (
), and proxy host config (docker inspect
).python3 npm_api.py --host-search <domain> - Common failure modes:
| Symptom | Check |
|---|---|
| 502 Bad Gateway | Container not on , wrong port, container stopped |
| SSL errors | Certificate missing/expired, domain mismatch |
| Redirect loop | ssl_forced=true on backend that also redirects to HTTPS |
| WebSocket failure | allow_websocket_upgrade not enabled |
| "Not found" / wrong app | forward_host or forward_port wrong |
| DNS not resolving | DNS record missing or not propagated |
- Connectivity test from NPM container:
docker exec <npm_container> curl -s http://<backend>:<port>/
Remove (destructive, double confirmation required)
Safety gates:
- User states intent.
- Agent shows: proxy host ID, domain, container name. For multiple items: numbered list, user selects by number, agent echo-backs selection. Waits.
- User must confirm with a phrase containing the word "remove" (e.g. "confirm remove").
Steps:
to identify targets.python3 npm_api.py --host-list- Apply multi-item selection protocol if multiple candidates.
for each confirmed target.python3 npm_api.py --host-delete <id>- Stop and remove containers. Warn before removing volumes (irreversible).
Cleanup (destructive, double confirmation required)
Removes orphan NPM proxy hosts (no running backend) and unused Docker resources.
Safety gates: same double confirmation as Remove; second confirmation must include "cleanup".
- Build candidate list:
then check each host's backend withpython3 npm_api.py --host-list
. List stopped containers, dangling images, unused networks.docker ps - Present numbered list. User selects by number. Agent echoes back selection.
- After "confirm cleanup":
for each orphan, then targeted Docker prune commands.python3 npm_api.py --host-delete <id>
Warn separately before
docker volume prune (data loss risk).
Backup and Restore
Backup is non-destructive; no double confirmation required.
Restore is destructive: show exactly what will be overwritten, require double confirmation with phrase containing "restore".
Backup:
(writes proxy hosts, users, settings, access lists, and certificates topython3 npm_api.py --backup
).DATA_DIR/<ip_port>/backups/<timestamp>/- Copy compose/stack files to
.~/.localsetup/backups/compose/
Restore: NPM does not expose a REST restore endpoint. Re-import from backup by re-running the Create workflow for each proxy host using the saved
proxy_hosts.json. For certificates, re-attach via NPM UI.
Agent behavior rules
- Confirm NPM API is reachable before any workflow:
.python3 npm_api.py --info - Know
for the target environment before running Create or Modify.<NPM_NETWORK> - Always use Docker container name as
for containers onforward_host
. Never use container IPs.<NPM_NETWORK> - After creating a proxy host, always prompt user about SSL certificate attachment.
- For multi-item destructive operations: numbered list, user selection by number, echo-back, named confirmation phrase.
- If
exits non-zero, the STDERR message is actionable; surface it to the user verbatim.npm_api.py - If the API token is stale,
auto-refreshes. If that fails, the error message will specify which config field to check (NGINX_IP, NGINX_PORT, API_USER, or API_PASS).npm_api.py - Set
before the command for full HTTP trace output when diagnosing connectivity issues.LOCALSETUP_DEBUG=1 - Warn if a container is not found on
when creating a proxy host.<NPM_NETWORK>
Placeholders reference
| Placeholder | Meaning | Example |
|---|---|---|
| Root of tools directory | |
| Path to npm_api.py | |
| Shared Docker network name | |
| Backup directory for compose files | |
Security
is gitignored; permissions must benpm-api.conf
.600- NPM admin port (81) must not be exposed to the internet.
- Use a strong NPM admin password.
- The
backup directory may contain SSL private keys; restrict permissions accordingly.data/
Reference
- scripts/npm_api.py - Native Python client (source of truth)
- references/npm-api-conf-example.md - Config file template and field reference
- references/proxy-host-template.md - Full default template with all fields
- https://github.com/NginxProxyManager/nginx-proxy-manager - NPM project (API source)
- https://github.com/NginxProxyManager/nginx-proxy-manager/tree/develop/backend/schema - NPM API schema