Sign inSign up

mod4ever/npm-auto-discovery

By mod4ever

•Updated 3 days ago

Auto-syncs Nginx Proxy Manager hosts via Docker VIRTUAL_HOST labels with self-healing.

Image
Networking
Monitoring & observability
0

676

mod4ever/npm-auto-discovery repository overview

⁠npm-auto-discovery

CI

Automatically discovers Docker containers and creates Nginx Proxy Manager proxy hosts.

⁠How It Works

npm-auto-discovery monitors the Docker event stream for container start events, reads VIRTUAL_HOST environment variables from running containers, and automatically calls the NPM API to create or update proxy hosts. A periodic self-healing reconcile loop (default: every 5 minutes) ensures state consistency after NPM downtime or missed Docker events — without any manual intervention.

⁠Quickstart

1. Clone the repository or copy the docker-compose.yml:

services:
  npm-auto-discovery:
    image: ghcr.io/mod4ever/npm-auto-discovery:latest
    restart: unless-stopped
    env_file: .env
    group_add:
      - "${DOCKER_GID:-996}"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy-network
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost:${HEALTH_PORT:-8080}/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

networks:
  proxy-network:
    external: true
    name: ${PROXY_NETWORK_NAME:-proxy-network}

2. Create a .env file with the 3 required variables:

NPM_URL=http://your-npm-host:81
[email protected]
NPM_PASSWORD=yourpassword

Copy the full template: cp .env.example .env

3. Start the service:

docker compose up -d

⁠Container Configuration

Add these environment variables to any container you want proxied through NPM:

services:
  myapp:
    image: myapp:latest
    environment:
      VIRTUAL_HOST: "myapp.example.com"     # Required — triggers proxy host creation
      VIRTUAL_PORT: "8080"                   # Optional — default: 80
      VIRTUAL_SSL_CERT_ID: "42"              # Optional — use existing NPM cert by ID
    networks:
      - proxy-network  # Must be on the same network as npm-auto-discovery
VariableRequiredDescription
VIRTUAL_HOSTYESComma-separated domain(s) to proxy (e.g., app.example.com,www.app.example.com)
VIRTUAL_PORTnoContainer port to forward traffic to (default: 80)
VIRTUAL_SSL_CERT_IDnoID of an existing NPM SSL certificate to attach
FORWARD_HOST_MODEnoPer-container override of the global FORWARD_HOST_MODE setting (auto|dns|ip)

⁠Environment Variables

VariableTypeRequiredDefaultDescription
NPM_URLURLYES—Base URL of Nginx Proxy Manager API (e.g., http://npm:81)
NPM_EMAILstring (email)YES—NPM admin account email
NPM_PASSWORDstringYES—NPM admin account password
NPM_CREDENTIALS_FILEpathno—Path to Docker secret file with NPM_EMAIL and NPM_PASSWORD (KEY=VALUE format)
RECONCILE_INTERVAL_MSnumber (ms)no300000How often the reconcile loop runs (5 min)
FORWARD_HOST_MODEauto|dns|ipnoautoContainer hostname resolution: auto=DNS-first+IP-fallback, dns=DNS only, ip=IP only
PROXY_NETWORK_NAMEstringnoproxy-networkDocker network name shared between this service, NPM, and managed containers
NPM_MAX_RETRIESnumberno3Maximum NPM API retry attempts with exponential backoff
HEALTH_PORTnumber (port)no8080Port for the /health endpoint
LOG_LEVELstringnoinfoPino log level (trace, debug, info, warn, error, fatal)

⁠CLI Options

When running the binary directly (SEA or PM2), these flags are available:

FlagDescription
-v, --versionPrint version and exit
-h, --helpPrint version, all environment variables with defaults, and exit
./npm-auto-discovery --version   # e.g. 1.0.0
./npm-auto-discovery --help

⁠Network Requirements

npm-auto-discovery must share a Docker network with both NPM and all managed containers. This allows the service to:

  1. Call the NPM API (requires network connectivity to NPM)
  2. Resolve container hostnames or IPs for proxy forwarding

The PROXY_NETWORK_NAME variable (default: proxy-network) must match the Docker network name configured in NPM and used by your managed containers.

Typical setup:

networks:
  proxy-network:
    external: true
    name: proxy-network

All three services — NPM, npm-auto-discovery, and your managed containers — must be connected to this network.

⁠Troubleshooting

All errors are logged as structured JSON with an error_code field.

# Docker
docker logs npm-auto-discovery | grep error_code

# PM2
npm run pm2:logs -- --lines 500 | grep error_code
# or search the raw log file
grep error_code /root/.pm2/logs/npm-auto-discovery-out-0.log
Error CodeTrigger ConditionOperator Resolution
NPM_UNREACHABLENPM API cannot be reached (network error, NPM down)Check NPM_URL value; verify npm-auto-discovery and NPM are on the same Docker network; check NPM is running
NPM_AUTH_FAILEDNPM API returns 401 on authenticationVerify NPM_EMAIL and NPM_PASSWORD are correct; check NPM user account is active
NPM_API_ERRORNPM API returned unexpected response or HTTP 5xxCheck NPM logs for errors; verify NPM version is 2.10.x+; check NPM_MAX_RETRIES
CERT_RATE_LIMITLet's Encrypt rate limit hit — too many certificate requests for this domainWait 1 hour before retrying (failed-validation rate limit is 5/hour per hostname); use VIRTUAL_SSL_CERT_ID to assign an existing certificate instead
CERT_FAILEDLet's Encrypt certificate request failed (DNS, port 80 challenge)Verify domain is publicly accessible; check port 80 is open for ACME challenge; verify DNS propagation
DOCKER_SOCKET_ERRORDocker daemon socket unavailable or permission deniedVerify /var/run/docker.sock is mounted read-only in the container; check Docker daemon is running
CONTAINER_NO_IPCannot determine container's forward host via DNS or IPEnsure container and npm-auto-discovery are on the same Docker network (PROXY_NETWORK_NAME); check FORWARD_HOST_MODE
PROXY_HOST_EXISTSA proxy host already exists for the domain with matching configuration (informational)Not an error — logged as result: "skipped" when startup scan or reconcile finds an existing proxy host that is already up-to-date
CONFIG_VALIDATION_ERRORA required environment variable is missing or invalid at startupCheck the value reported in the log's field key; verify all required variables (NPM_URL, NPM_EMAIL, NPM_PASSWORD) are set and valid

⁠Compatibility

  • Nginx Proxy Manager: jc21/nginx-proxy-manager 2.10.x+
  • Docker Engine API: v1.41+ (Docker 20.10+)
  • Node.js: 24 LTS

⁠PM2 Deployment

For non-Docker environments, deploy using PM2. Two variants are available:

⁠SEA Binary (default — no Node.js runtime on PATH required)
# Install PM2 globally
npm install -g pm2

# Install dependencies, then build the self-contained binary
npm install
npm run build:sea

# Configure credentials in .env (copy from template)
cp .env.example .env
# Edit .env: set NPM_URL, NPM_EMAIL, NPM_PASSWORD

# Start with PM2 (uses pm2.sea.config.cjs)
npm run pm2:start

# Save process list for auto-restart on reboot
npm run pm2:save
pm2 startup   # generates the system startup command — run the printed command as root

Typical update workflow:

npm run build:sea && npm run pm2:restart
⁠Node.js Runtime (alternative — requires Node.js 24 on PATH)
npm install
npm run build
npm run pm2:start:node

Typical update workflow:

npm run build && npm run pm2:restart
⁠PM2 Scripts
ScriptDescription
npm run pm2:startStart via SEA binary (pm2.sea.config.cjs) — default
npm run pm2:start:nodeStart via Node.js runtime (pm2.node.config.cjs)
npm run pm2:stopStop the service
npm run pm2:restartRestart (works for both variants)
npm run pm2:statusShow status, uptime, and memory
npm run pm2:logsLive log tail with human-readable output
npm run pm2:savePersist process list across reboots

SEA variant (pm2:start): pm2:start sources .env into the shell before starting PM2 (set -a && . ./.env), so PM2 inherits and passes all vars to the binary. .env must exist.

Node.js variant (pm2:start:node): env vars are loaded via Node.js --env-file in node_args (see pm2.node.config.cjs).

Both variants also accept vars set directly in the env block of their config file.

⁠Node.js 24 SEA Binary

Build a self-contained binary with no Node.js runtime requirement (Linux):

# Step 1: Build CJS bundle
npm run build

# Step 2: Generate SEA preparation blob
# Requires dist/main.cjs from Step 1 — run npm run build first if skipping Step 1
node --experimental-sea-config sea-config.json

# Step 3: Copy the node binary
cp $(which node) npm-auto-discovery

# Step 4: Inject the blob into the binary
npx postject npm-auto-discovery NODE_SEA_BLOB sea-prep.blob \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2

# Step 5: Run the binary
NPM_URL=http://npm:81 [email protected] NPM_PASSWORD=pass \
  ./npm-auto-discovery

For macOS, additional codesign steps are required. See Node.js 24 SEA documentation⁠ for platform-specific instructions.

Or run all steps at once:

npm run build:sea

⁠Development

# Install dependencies
npm install

# Run in development mode (hot-reload via tsx)
npm run dev

# Run tests with coverage
npm test

# Build production bundle
npm run build

# Type-check + lint
npm run check

⁠Changelog

See CHANGELOG.md⁠ for a full history of changes.

⁠License

AGPL-3.0-only⁠

Tag summary

Content type

Image

Digest

sha256:4c7696016…

Size

68.1 MB

Last updated

3 days ago

docker pull mod4ever/npm-auto-discovery