Skip to content

Feature/add load balancing - #5727

Open
jakubstrama wants to merge 3 commits into
NginxProxyManager:developfrom
jakubstrama:feature/add-load-balancing
Open

jakubstrama wants to merge 3 commits into
NginxProxyManager:developfrom
jakubstrama:feature/add-load-balancing

Conversation

@jakubstrama

Copy link
Copy Markdown

Why

Nginx supports upstream load balancing via upstream {} blocks, allowing a proxy host to distribute traffic across multiple backend servers. Currently, NPM only supports a single forward_host:forward_port target per proxy host, with no way to configure multiple servers or a balancing strategy.

This PR adds full Nginx upstream load balancing support to proxy hosts.

Balancing methods (mapped to Nginx directives)

  • Round Robin — default, no directive needed
  • Least Connectionsleast_conn;
  • IP Hash (sticky sessions)ip_hash;

Per-server parameters

  • weight — relative request weight for round-robin distribution
  • max_fails / fail_timeout — passive health check thresholds
  • backup — receive requests only when all primary servers are down
  • down — permanently mark a server unavailable (graceful removal)

The Nginx template generates an upstream npm-<id> {} block at the top of each proxy host configuration file (valid at the http context level) and switches proxy_pass to target the upstream group when upstream servers are configured.

Existing single-target proxy hosts remain unaffected.

API changes

Two new optional fields are accepted when creating or updating a proxy host:

  • lb_method (enum)
  • upstream_servers (array of objects)

Both default to an empty configuration using Round Robin, so existing integrations require no changes.

Constraint enforcement

Nginx does not allow the backup flag to be used together with ip_hash. This constraint is enforced in both the backend and frontend:

  • Backend: returns 400 Bad Request before attempting to update the Nginx configuration.
  • Frontend: the Backup option is automatically disabled and cleared when IP Hash is selected.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • API changes
  • Performance improvement
  • Test addition or update

AI Usage

  • AI was used to write this.
  • AI was used to review this.

@nginxproxymanagerci

Copy link
Copy Markdown

Docker Image for build 2 is available on DockerHub:

nginxproxymanager/nginx-proxy-manager-dev:pr-5727

Note

Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
This is a different docker image namespace than the official image.

Warning

Changes and additions to DNS Providers require verification by at least 2 members of the community!

@GabrielFernandes-Dev

Copy link
Copy Markdown

Any plans on merging this PR or there is a problem with it ?

@jakubstrama

Copy link
Copy Markdown
Author

It never even got reviewed.

@jc21

jc21 commented Sep 23, 2026

Copy link
Copy Markdown
Member

It never even got reviewed.

Your PR isn't the only one that hasn't been reviewed in a timely manner. Better late than never!

  1. Please don't commit lock files. You're not modifying node packages in this PR so there's no need. Dependabot and the usual dependency updates take care of this, and your PR can potentially undo some of those updates.
  2. backend/migrations/20260720000000_proxy_host_upstream.js:19 — The older migrations that add JSON columns don't set a default. This passes in CI with MySQL, but I want to be consistent.
  3. backend/templates/proxy_host.conf:12 — Invalid nginx config from API requests. The schema only requires host and port, and the validator doesn't fill in the schema's default values. A server sent without max_fails or fail_timeout renders as max_fails= fail_timeout=;, which nginx rejects. The frontend always sends these fields; API clients may not.
  4. backend/templates/proxy_host.conf:20 — Custom config that uses $port breaks. In upstream mode the template no longer sets $port. Any advanced config that uses include conf.d/include/proxy.conf or $server:$port ends up with a broken proxy_pass address.
  5. backend/templates/proxy_host.conf:6 — One missing backend can take down every host. Hostnames in an upstream {} block are looked up when nginx loads its config, not per request as the current setup does. If one upstream host can't be resolved (for example a stopped Docker container), nginx fails to load and every proxy host goes down.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants